"""
-----------------------------------------------------------------------------
Copyright (c) 2009-2014 Alexandru Emilian Susu (icam.service@gmail.com).
    License: BSD.
    Ideas from many :) .

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
    ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
    OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
    OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
    SUCH DAMAGE.


The code tries to be PEP8 compliant with one main exception:
    - since we have the ambiguity 0 == False, 1 == True (~inherited from C),
        when a == 1, "a is True" returns False; a == True returns True
            (if a: is evaluated as True, also)
      - similarly for a == 0, "a is False" returns False; a == False returns True
        Therefore we do not use "is True" (but "== True") 
            nor "is False" (instead we use "== False").
-----------------------------------------------------------------------------
"""

"""
Platforms supported:
  Note that the predicates for the various OS platforms defined in iCam.py are:
    ANDROID_OS
    RASPBIAN_OS
    SYMBIAN_S60_OS
    SYMBIAN_UIQ_OS
    SYMBIAN_3
    iOS_PYOBJC
    WINDOWS_OS
    WINDOWS_CE_OS_PYTHONCE

    You can use the ppp.py tool to obtain from iCam.py a script for only one
      (or more) platform.

    Examples of how NOT TO use them:
      NOT GOOD:
        elif SYMBIAN_3
            ...
        elif SYMBIAN_OS and (S60_EDITION[0] >= 3):
            ...

     -instead use:
        elif SYMBIAN_OS:
            if SYMBIAN_3:
                ...
            else (S60_EDITION[0] >= 3):
                ...

      NOT GOOD:
        if ANDROID_OS is False:
            ...

     -instead use:
        if ANDROID_OS
            pass
        else:
            ...

We use the 3 double-quotes to represent ONLY comments that can be discarded,
    and not literal strings.
"""

"""
This Python source file is the cross-platform iCam phone client, working for:
    ANDROID_OS
    SYMBIAN_S60_OS
    SYMBIAN_UIQ_OS
    iOS_PYOBJC
    WINDOWS_OS
    WINDOWS_CE_OS_PYTHONCE.

Note that we make the STRONG assumption that:
    - for S60 2nd edition phones we run over PyS60 1.4.5
    - for S60 3nd edition phones we run over PyS60 2.0.0 (except Nokia E60 and
        E65 on which we cannot install this runtime and we have to use
        PyS60 1.4.5; also, maybe, non-Nokia phones such as Samsung G810)

After modifying this file, the easiest way to deploy it is to do the following:
    - on Android, after installing iCam.apk, compile into byte-code this file
        (normally with Python 2.6) and put the byte-code file
        in /sdcard/iCam/a.pyc)
    - on S60 3rd edition, after installing iCam[...].sis, you can simply copy
        the Python source to [DRIVE]:\Private\e21e55ef\default.py, where
        [DRIVE] is the drive where you selected to install the SIS.
    - iOS PyObjC - just compile into byte-code (normally with Python 2.5) this
        file and replace the old a.pyc with the generated bytecode.
    - Windows PythonCE - just compile into byte-code (normally with Python 2.5)
        this file and replace the old a.pyc with the generated bytecode.

Also, you ~need to copy the iCam.cfg in the LOCAL_FOLDER of the application.
"""


"""
CRC32 could be added to the BT messages as footer CRC: BUT I DON'T
    - BT SMF messages - have already CRC in the state part, which is VERY good, media could be corrupted
    - FIL - would be useful - but it is already compressed with zlib
    - CMD - not really useful
    - TXT - not really useful - if error happens, that's it
    - TIM - not required since message does NOT contain data.

"""

CURRENT_RELEASE_TIME = "2015_01_04_15_00_00"

"""
We execute before Main() due to lexical order in the script (even when we import this script):
    - a lot of initializations for global vars
    - imports, with exception handling
    - more complex code (function calls, loops, etc) - these are marked in 
        the code as "# BEFORE_MAIN:".
"""

"""
New BT packet format: we do not compress BT messages and put footer extra info
    in SMF in order to allow playing file even in Inbox
"""
#NEW_BT_FORMAT = False
NEW_BT_FORMAT = True



IMEI_UIQOS   = "UIQ-device-id"
IMEI_IOS     = "iPhone-device-id"
IMEI_UNIXOS  = "*nix-device-id"
IMEI_WinCEOS = "Win-mobile-device-id"
IMEI_WinOS   = "Win-device-id"
IMEI_6680    = "668066806680668"
IMEI_6120    = "612061206120612"
IMEI_N95     = "N95N95N95N95N95"
IMEI_N82     = "N82N82N82N82N82"
IMEI_G810    = "300300300300300"
IMEI_E7      = "E7E7E7E7E7E7E7E"
IMEI_HTC_TC  = "35598001238745601" #HTC P3650

BT_ADDR_6680    = "66:80:66:80:66:80"
BT_ADDR_6120    = "61:20:61:20:61:20"
BT_ADDR_N82     = "82:82:82:82:82:82"
BT_ADDR_N95     = "95:95:95:95:95:95"
BT_ADDR_HTC_TC  = "36:50:36:50:36:50" #HTC P3650
BT_ADDR_RPI     = "PI:PI:PI:PI:PI:PI"

WIFI_ADDR_N82 = "WI:FI:WI:FI:WI:FI"

# PHONE NUMBER which we call (can be a landline number also).
ALARM_PHONE_NUMBER = "+01234567890" # "+01234567890" #"1234567890"
# PHONE NUMBER which we call or send SMS to.
ALARM_SMS_PHONE_NUMBER = "+01234567890"

# Used by StoreLocalConfigInFile() and GetGooglePassword()
# A 32 bytes long randomly generated (by hand :) ) secret key
AES_SECRET_KEY = "12345678901234567890123456789012"

# AES_SECRET_KEY = AES_SECRET_KEY[:32]
# From C:\Python25\Lib\site-packages\gdata\tlslite\utils\cipherfactory.py
#    "param IV: A 16 byte string"
AES_IV = "1234567890123456"

# Used in IQEnginesPhotoUpload().
IQE_KEY = ""
IQE_SECRET = ""

# Used in ConnectToYouTubeGData(), YouTubeVideoUpload()
#YOUTUBE_TEST_CLIENT_ID = ""
YOUTUBE_TEST_CLIENT_ID = "ytapi-pythonclientlibrary_servicetest"

# Used in ConnectToYouTubeGData(), YouTubeVideoUpload()
# Mobile ReVival YouTube developer key
#youtubeDeveloperKey = ""
youtubeDeveloperKey = "AI39si6eJ7T_TA4Xwl-jZkLDjxi83JPvhgt46Q6k_QfCRWbpW5izuvHCDIw6hjC_CDzyhfPIbisquvtcj0mlotr6ejJGSXA_lg"

"""
It seems that the main overhead is not the display but the storage, Inet
    transmission and maybe the detection algorithm.
"""
NUM_FRAMES_TO_UPDATE_VIEWFINDER_EVERY = 1  # 10

startViewfinderBeforeTakingPhoto = 0  # 1

ADD_VIDEO_TO_PLAYLIST = False

BATTERY_LEVEL_THRESHOLD = 40

# PyUIQ 0.2?? uses Python 2.2.2 (PyS60 1.3.18).
#    The modules telephone, sysinfo, camera, audio are not working.

###############################################################################
###############################################################################
###############################################################################
###########################PLATFORM DEPENDENT ISSUES###########################
###############################################################################
###############################################################################
import os
import sys



# TODO (low-priority): discover more reliable if we run on RPi or not
# See http://www.raspberrypi.org/forums/viewtopic.php?f=32&t=54413 for other ideas - /proc/cpuinfo
try:
    import RPi.GPIO
    RASPBIAN_OS = True
except:
    RASPBIAN_OS = False

# sys.path.insert(0, "E:\\lib.zip")

#import traceback #!!!!TODO: remove
try:
    try:
        import android
        #print "dir(android) =", dir(android)
        myDroid = android.Android()
        ANDROID_OS_QPYTHON = False
    except:
        #traceback.print_exc()

        import androidhelper
        myDroid = androidhelper.Android()
        ANDROID_OS_QPYTHON = True

    ANDROID_OS = True

    #myDroid.makeToast("Alex test.") # This works here OK
except:
    #traceback.print_exc()
    #print "Couldn't import androidhelper module."
    ANDROID_OS = False

try:
    import objc
    iOS_PYOBJC = True
except:
    #print("Couldn't import the android module.")
    iOS_PYOBJC = False


"""
We add lib_std.zip in the sys.path to be able to import the GData API
    library, which we install apriori in the current directory.
"""
# sys.path.insert(0, os.getcwd())
# sys.path.insert(0, os.path.join(os.getcwd(), "gdata.zip"))
#if ANDROID_OS == False:
if ANDROID_OS:
    pass
elif iOS_PYOBJC:
    # So far, sys.path[0] was
    #    '/private/var/stash/Applications.k14UJu/HelloPython.app' and
    #    os.getcwd() returns "/". But I fear!!!! that this is not always
    #    like this.
    # sys.path.insert(0, os.path.join(sys.path[0], "lib_std.zip"))
    sys.path.append(os.path.join(sys.path[0], "lib_std.zip"))
else:
    # This includes Symbian, in general (although PyS60 1.4.5 can't read the ZIP)
    sys.path.insert(0, os.path.join(os.getcwd(), "lib_std.zip"))

# print "sys.path = ", sys.path
# sys.stdout.flush()

SYMBIAN_OS = False
SYMBIAN_S60_OS = True
SYMBIAN_UIQ_OS = False  # True
SYMBIAN_S60_2ND_ED = False
SYMBIAN_S60_3RD_ED = False
SYMBIAN_1 = False
SYMBIAN_3 = False
try:
    import e32

    # Both PyS60 and PyUIQ have the e32 module --> SYMBIAN_OS = True for
    #    both S60 and UIQ.
    SYMBIAN_OS = True

    S60_EDITION = e32.s60_version_info

    if ((S60_EDITION[0] == 5) and (S60_EDITION[1] == 3)) or \
            ((S60_EDITION[0] == 5) and (S60_EDITION[1] == 2)):
        # Symbian Belle
        # What is the value for Symbian Anna?
        SYMBIAN_3 = True
    else:
        SYMBIAN_3 = False

    if S60_EDITION[0] == 5 and S60_EDITION[1] < 2:
        SYMBIAN_1 = True
    else:
        SYMBIAN_1 = False

    if S60_EDITION[0] == 3:
        SYMBIAN_S60_3RD_ED = True
    else:
        SYMBIAN_S60_3RD_ED = False

    if S60_EDITION[0] == 2:
        SYMBIAN_S60_2ND_ED = True
    else:
        SYMBIAN_S60_2ND_ED = False
except:
    """
    if MY_DEBUG_STDERR:
        traceback.print_exc()
        sys.stderr.flush()
    """
    # print "Couldn't import Symbian PyS60 module e32."
    SYMBIAN_OS = False
    SYMBIAN_S60_OS = False
    SYMBIAN_UIQ_OS = False

WINDOWS_OS = False
WINDOWS_CE_OS_PYTHONCE = False
try:
    if sys.platform == "win32":
        if os.path.exists("/Storage Card"):
            print "Running on Win32 (WinCE)"
            WINDOWS_CE_OS_PYTHONCE = True
        else:
            print "Running on Win32 (Win for desktop :) )"
            WINDOWS_OS = True
except:
    print("Error around sys.platform.")

#UNIX_OS = True
UNIX_OS = False

###############################################################################
###############################################################################
###############################################################################
###############################################################################
###############################################################################
##############################START PROGRAM####################################
###############################################################################
###############################################################################
###############################################################################
###############################################################################
###############################################################################

# Important vars:
"""
I currently use this for Nokia 6680 and N95, when in BT client mode, since I want:
    - not to resize photos but send them original (since 6680 has little mem
        and might go Out of memory),
    - the state.bin and log files are stored on E:
      BUT
      - the photos need to be saved in D: since E: sometimes gives
            error messages
    - to erase the media file after sending it (even if not successful??).

This instructs phone not to resize photos taken and send them
     at original resolution directly.
"""
MODE_FOR_PHONE_WITH_LITTLE_RAM_AND_UNRELIABLE_MEM_CARD = False

MY_DEBUG_STDOUT = True
MY_DEBUG_STDERR = True
MY_DEBUG_STDERR_2 = True
# For uploaded log messages
MY_DEBUG_UPLOAD_MSG = True

#USE_ICAM_SERVER = True
#USE_ICAM_SERVER = False

MEGA_BYTE = 1024 * 1024

# import os.path
if SYMBIAN_OS:
    #try:
    import appuifw

    # if SYMBIAN_OS:
    if SYMBIAN_S60_OS:
        # Even if PyUIQ 2.0 has libs\sysinfo.py it doesn't have _sysinfo.pyd
        import sysinfo
    #except:
    #    DebugPrintErrorTrace()

    def DoesDriveExist(driveStr):
        """
        if MY_DEBUG_STDOUT:
            print "e32.drive_list() =", e32.drive_list()
            print "os.path.exists('E:\\') =", os.path.exists("E:\\")
            print "sysinfo.free_drivespace() =", sysinfo.free_drivespace()
            sys.stdout.flush()
        """

        # Inspired from IYouIt :)
        if os.path.exists(driveStr + "\\") and \
                    (unicode(driveStr) in e32.drive_list()) and \
                    sysinfo.free_drivespace().has_key(unicode(driveStr)):
            return True
        return False

    # DoesDriveExist("E:")

LOCAL_DRIVE = ""
LOCAL_FOLDER_TEMP = None
# define ANDROID_OS
# ifdef ANDROID_OS
# endif
if SYMBIAN_OS:
# if SYMBIAN_OS and SYMBIAN_S60_OS:
    LOCAL_FOLDER_TEMP = "D:/iCam"

    if SYMBIAN_UIQ_OS:
        if os.path.exists("D:\\"):
            LOCAL_DRIVE = "D:"
    elif DoesDriveExist("E:"):
        LOCAL_DRIVE = "E:"
    elif DoesDriveExist("F:"):
        LOCAL_DRIVE = "F:"
    elif DoesDriveExist("C:"):
        appuifw.note(u"No E or F drives found. Using drive C to store " \
                        "iCam folder.", "info")
        LOCAL_DRIVE = "C:"
    else:
        appuifw.note(u"There are no C, E or F drives on this system :O ...",
                    "info")
        LOCAL_DRIVE = ""
elif ANDROID_OS:
    # """
    if os.path.exists("/mnt/sdcard/extsd"): # /sdcard/extsd
        LOCAL_DRIVE = "/mnt/sdcard/extsd"
    elif os.path.exists("/mnt/sdcard/external_sd"):
        LOCAL_DRIVE = "/mnt/sdcard/external_sd"
    elif os.path.exists("/sdcard"):
        LOCAL_DRIVE = "/sdcard"
    else:
        LOCAL_DRIVE = "/" #!!!! should it be "" - cause we add "/iCam", etc at it?!!!!
elif iOS_PYOBJC:
    # It seems /tmp is erased on iOS upon reboot :)
    # if os.path.exists("/tmp"):
    #    LOCAL_DRIVE = "/tmp"

    # The bigger drive is at /private/var which is sym-linked at /var
    #    (df -H reports:
    #           /dev/disk0s2           3.6G   287M   3.3G   9% /private/var)

    # Note: the CameraRoll stores photos in /var/mobile/Media/DCIM/100APPLE/
    if os.path.exists("/var/mobile"):
        LOCAL_DRIVE = "/var/mobile"
    else:
        # It seems I don't have access to create "/iCam"!!!!
        LOCAL_DRIVE = "/"
elif WINDOWS_OS:
    if os.path.exists("Z:\\"):
        LOCAL_DRIVE = "Z:"
    else:
        LOCAL_DRIVE = "C:"

    if os.path.exists("R:\\iCam"):
        LOCAL_FOLDER_TEMP = "R:\\iCam"
    elif os.path.exists("Z:\\iCam"):
        LOCAL_FOLDER_TEMP = "Z:\\iCam"
    else:
        LOCAL_FOLDER_TEMP = "C:\\iCam"
elif UNIX_OS:
    LOCAL_DRIVE = "." # "./"
elif WINDOWS_CE_OS_PYTHONCE:
    if os.path.exists("/Storage Card"):
        # Note: \Storage Card/iCam/Unsent works on PythonCE
        #    (e.g., Z:\1PhD\ReVival\Logs\WinCE\2011_03_21)
        #    (but \Storage Card/iCam/Unsent/2011_03_20_23_51_49_000.txm
        #      doesn't - e.g., in
        #      Z:\1PhD\ReVival\Logs\WinCE\WiFi_not_working\stderr_2011_03_20_23_51_45.txt)
        LOCAL_DRIVE = "/Storage Card"
    else:
        #LOCAL_DRIVE = "/"  # "\\My Device"
        LOCAL_DRIVE = ""  # "\\My Device"
elif RASPBIAN_OS:
    LOCAL_DRIVE = "/home/pi" # "./"


ICAM_APP_TITLE = u"iCam"
MENU_SELECT_PREFIX = "* "

# LOCAL_FOLDER = "E:/ReVival"
# LOCAL_FOLDER = "F:/iCam"
LOCAL_FOLDER = LOCAL_DRIVE + "/iCam"
if LOCAL_FOLDER_TEMP is None:
    LOCAL_FOLDER_TEMP = LOCAL_FOLDER

"""
This is useful in case we have pauseInterval == 0, since it allows very fast
    writing of files (JPEGs and .smf, etc).
"""
# LOCAL_FOLDER = "D:/iCam"

LOCAL_FOLDER_MEDIA_FILES = LOCAL_FOLDER + "/Media"
LOCAL_FOLDER_SENT_LOGS = LOCAL_FOLDER + "/LogsSent"

WINCE_MEDIA_FOLDER = "/Storage Card/DCIM/100MEDIA"
WINCE_SENT_MEDIA_FOLDER = WINCE_MEDIA_FOLDER + "/Sent"

ERASE_ORIGINAL_MEDIA_FILE_AFTER_READ = False

LOCAL_FOLDER_UNSENT_FILES = LOCAL_FOLDER + "/Unsent"

STATE_FILENAME = "state.bin"
STATE_BACKUP_EXTENSION = ".bak"
STATE_PATH_FILENAME = LOCAL_FOLDER + "/" + STATE_FILENAME
STATE_PATH_FILENAME_BACKUP = STATE_PATH_FILENAME + STATE_BACKUP_EXTENSION

LOCAL_CONFIG_FILENAME = "iCam.cfg"
LOCAL_CONFIG_PATH_FILENAME = LOCAL_FOLDER + "/" + LOCAL_CONFIG_FILENAME

STDERR_FILENAME_PREFIX = "stderr_"
STDOUT_FILENAME_PREFIX = "stdout_"

INTRANET_SERVER = False
#INTRANET_SERVER = True
USE_ICAMSRV_GAE = False
#USE_ICAMSRV_GAE = True

ICAM_GAE_SERVER_NAME = "icamsrv.appspot.com"

if INTRANET_SERVER == False:
    if USE_ICAMSRV_GAE == False:
        ICAM_SERVER_NAME = "mobile-revival.110mb.com"
        WEB_FOLDER = "/ReVival"
        """
        ICAM_SERVER_NAME = "alexsusu.110mb.com"
        WEB_FOLDER = "/iCam"
        """
    else:
        ICAM_SERVER_NAME = ICAM_GAE_SERVER_NAME
        WEB_FOLDER = ""
else:
    if USE_ICAMSRV_GAE == False:
        # ICAM_SERVER_NAME = "83.149.75.83"
        # ICAM_SERVER_NAME = "aes123.110mb.com"
        """
        ICAM_SERVER_NAME = "192.168.2.205"  # Titan WiFi
        WEB_FOLDER = "/ReVival"
        """
        pass
    else:
        ICAM_SERVER_NAME = "localhost:8080"
        WEB_FOLDER = ""


# WEBPAGE_UL_GZIPPED_STATE_AND_FILE = WEB_FOLDER + "/UploadBinary.php"
# WEBPAGE_UL_GZIPPED_STATE_AND_FILE = WEB_FOLDER + "/UploadStateAndFile.php"
if USE_ICAMSRV_GAE:
    WEBPAGE_UL_GZIPPED_STATE_AND_FILE = WEB_FOLDER + "/proxyyoutube"
else:
    WEBPAGE_UL_GZIPPED_STATE_AND_FILE = WEB_FOLDER + \
                                        "/UploadGzippedStateAndFile.php"

WEBPAGE_UL_GZIPPED_STATE_AND_FILE_PROXY_YOUTUBE = "/proxyyoutube"
WEBPAGE_UL_GZIPPED_FILE = WEB_FOLDER + "/UploadFile.php"
# WEBPAGE_DL_GZIPPED_TEXT
WEBPAGE_DL_GZIPPED_FILE = WEB_FOLDER + "/DownloadFile.php"
# WEBPAGE_UL_GZIPPED_TEXT = "/video_surveillance_mobile_text.php"
if USE_ICAMSRV_GAE:
    WEBPAGE_UL_GZIPPED_TEXT = WEB_FOLDER + "/uploadgzippedtext"
else:
    WEBPAGE_UL_GZIPPED_TEXT = WEB_FOLDER + "/UploadGzippedText.php"

# WEBPAGE_DL_COMMAND_FILE = "/WiSurveillance/cmd.txt"
# WEBPAGE_DL_COMMAND_FILE = "/cmd.txt"
# WEBPAGE_DL_COMMAND_FILE = "/cmd.php"
WEBPAGE_DL_COMMAND_FILE = WEB_FOLDER + "/cmd.php"

# BT_OBEX_FILENAME_PREFIX = "sLog_"
#BT_OBEX_FILENAME_PREFIX = "iCam_"
BT_OBEX_FILENAME_PREFIX = "iC_"

# Used when NEW_BT_FORMAT == False:
BT_OBEX_FILENAME_PREFIX_TYPE_CMD = "CMD_"
BT_OBEX_FILENAME_PREFIX_TYPE_FIL = "FIL_"
BT_OBEX_FILENAME_PREFIX_TYPE_SMF = "SMF_"
BT_OBEX_FILENAME_PREFIX_TYPE_TXT = "TXT_"


COMMANDS_FILENAME = "cmd.txt"

# These are extensions for files transferred via Internet (not BT):
EXTENSION_COMMAND_MESSAGE = ".cmd"

# "fil" comes from arbitrary file - used for Unsent, Bluetooth, etc
EXTENSION_ARBITRARY_FILE = ".fil"

# "smf" comes from state and media file. These files are used for
#    Unsent (own files and for Bluetooth client).
EXTENSION_STATE_AND_MEDIA_FILE = ".smf"

# Text message (normally should be compressed, that's why it's not ".txt").
EXTENSION_TEXT_MESSAGE = ".txm"

# These are extensions for files transferred via BT:
BT_OBEX_EXTENSION_TXT = ".txt"
BT_OBEX_EXTENSION_CMD = ".cmd.txt"
BT_OBEX_EXTENSION_TIM = ".tim" # time sync message

##
#BT_OBEX_EXTENSION_LIST_CMD = [EXTENSION_COMMAND_MESSAGE]
BT_OBEX_EXTENSION_LIST_CMD = [BT_OBEX_EXTENSION_CMD]
BT_OBEX_EXTENSION_LIST_FIL = [EXTENSION_ARBITRARY_FILE]
#BT_OBEX_FILENAME_EXTENSION_TYPE_SMF = [".jpg", ".png", ".3gp", ".mp4", ".amr"]
BT_OBEX_EXTENSION_LIST_SMF = [".jpg", ".png", ".3gp", ".mp4", ".amr"]
#BT_OBEX_EXTENSION_LIST_TXT = [EXTENSION_TEXT_MESSAGE]
BT_OBEX_EXTENSION_LIST_TXT = [BT_OBEX_EXTENSION_TXT]


changedConserveEnergy = False
conserveEnergy = False

# 0 - None, 1 - All; 2 - wo .txm
saveUnsentPackets = 0
# saveUnsentPackets = True

"""
uploadUnsentData means:
    0 - send none;
    1 - send unsent files from Unsent (excepting .txm unsent files);
    2 - send unsent logs;
    3 - send ALL (excepting .txm  unsent files)
"""
uploadUnsentData = 0
NUM_UNSENT_PACKETS_BEFORE_DOWNLOAD_COMMANDS = 1  # 10

"""
Sensing accelerator (it is called 35?? times / sec) and rotation slows
    down iCam.
"""
logAccelerometerAndRotationSensors = False
startAutomatically = 0  # False
startButtonPressed = False
reactiveLoopIsStarted = False
"""
# It is stored as an int in the iCam state.
Note that we use it also to store for fact IMEI_N82 phone sent (or not) to
  IMEI_6680 the fact that its charger power went down (we assume both phones
  are connected at the same "power source"), in SleepAndPetWatchdog().
"""
reactiveLoopOpsIndex = 0
sentBTMessageTo6680 = 0

"""
Map from BT-MAC-address (NOT btClientDeviceId) to OPP-service-port number
"""
#bluetoothServerOPPServicePort = -1
bluetoothServerOPPServicePort = {}

###############################################################################
###############################################################################
###############################################################################
###########################STATE VARIABLES#####################################
###############################################################################
###############################################################################

import binascii
import time
import traceback


def DebugPrint(aText):
    try:
        if MY_DEBUG_STDOUT:
            print aText
            sys.stdout.flush()
    except:
        pass

def DebugPrintErrorTrace():
    try:
        if MY_DEBUG_STDERR:
            traceback.print_exc()
            sys.stderr.flush()
    except:
        pass


if WINDOWS_CE_OS_PYTHONCE:
    """
    From http://stackoverflow.com/questions/51658/cross-platform-space-remaining-on-volume-using-python
        (see also http://stackoverflow.com/questions/2973480/available-disk-space-on-an-smb-share-via-python
        and http://bytes.com/topic/python/answers/609682-how-check-remaining-hard-drive-space-windows)
    """
    try:
        import ctypes
        DebugPrint("Imported ctypes.")

        """
        From http://mail.python.org/pipermail/pythonce/attachments/20080906/e52a55e2/attachment-0001.py
            (see also http://old.nabble.com/Can-I-execute-external-programs-from-PythonCe--td19307784.html#a19357083)
        """
        CreateProcess = ctypes.cdll.coredll.CreateProcessW
        WaitForSingleObject = ctypes.cdll.coredll.WaitForSingleObject
        GetExitCodeProcess = ctypes.cdll.coredll.GetExitCodeProcess
        DWORD = HANDLE = ctypes.c_ulong
    except:
        DebugPrintErrorTrace()

    # Execute a new process and wait until it finishes - because of _wait_process(hPro)
    def WinSpawn(path, args):

        class _PI(ctypes.Structure):
            global DWORD, HANDLE
            _fields_ = [("hPro", HANDLE), ("hTh", HANDLE), ("idPro",
                        DWORD), ("idTh", DWORD)]

        def _create_process(cmd, args):
            pi = _PI()
            CreateProcess(
                    unicode(cmd),
                    unicode(args),
                    0,
                    0,
                    0,
                    0,
                    0,
                    0,
                    0,
                    ctypes.byref(pi)
                )

            return pi.hPro

        def _wait_process(hPro):
            WaitForSingleObject(hPro, ctypes.c_ulong(0xffffffff))
            return GetExitCodeProcess(hPro)

        def _quote(s):
            if " " in s:
                return '"%s"' % s
            return s

        def MyJoin(args, myChar):
            myRes = ""
            for i in range(len(args) - 1):
                myRes += _quote(args[i]) + myChar
            if len(args) >= 1:
                myRes += _quote(args[len(args) - 1])
            return myRes

        def execv(path, args):
            if not type(args) in (tuple, list):
                raise TypeError, "execv() arg 2 must be a tuple or list"

            path = os.path.abspath(path)
            """
            #This crashes pyobfuscate
            args = " ".join(_quote(arg) for arg in args)
            _create_process(path, args)
            """
            myArgs = MyJoin(args, " ")
            _create_process(path, myArgs)

        def execve(path, args, env):
            execv(path, args)

        DebugPrint("Entered WinSpawn().")

        try:
            # Was called systema before - def systema(path, args):
            if not type(args) in (tuple, list):
                raise TypeError, \
                    "systema() arg 2 must be a tuple or list"
            path = os.path.abspath(path)
            """
            args = " ".join(_quote(arg) for arg in args)
            hPro = _create_process(path, args)
            """
            myArgs = MyJoin(args, " ")
            hPro = _create_process(path, myArgs)
            return _wait_process(hPro)
        except:
            DebugPrintErrorTrace()
elif WINDOWS_OS:
    try:
        import ctypes
    except:
        DebugPrintErrorTrace()

###############################################################################
###############################################################################
###############################################################################
###############################################################################

# This is for the case when there is no SIM card in the cellphone.
NO_GSM_SIGNAL_STRENGTH = -1234
signalStrength = NO_GSM_SIGNAL_STRENGTH
signalUnits = ""
accessPointName = u""
"""
If accessPointRetryConnect is True it means that connection to the AP
    accessPointName was not successful. And this leads to retrying
    connection to the AP.
"""
accessPointRetryConnect = False


def NoInternetConnection():
    global accessPointName, accessPointRetryConnect

    return (accessPointRetryConnect == True) or (accessPointName == u"")


# if SYMBIAN_OS:
if SYMBIAN_S60_OS:
    import globalui


def DisplayNote(aText, waitTime=2.0):
    if SYMBIAN_OS:
        if waitTime == 2.0:
            # appuifw.note(unicode(aText), "info")
            # appuifw.note(unicode(aText), "error")
            appuifw.note(unicode(aText), "conf")
        elif waitTime == -1.0:
            """
            Good
            From http://wiki.forum.nokia.com/index.php/Python_on_Symbian/04._Basic_User_Interface.
            The ONLY problems with this one is that it has a progress bar and a
                Cancel button instead of OK.
            """
            globalui.global_note(unicode(aText), "wait")
    elif ANDROID_OS:
        # It's shown only for 2 secs
        #globalui.global_note(u"Item available", "info")

        # It's shown only for 2 secs
        #globalui.global_note(u"Message", "text")

        # It is really permanent note - you have to restart Symbian after this :))
        #globalui.global_note(unicode("Your device ID (IMEI): " + deviceId + "."), "perm")

        """
        Quite good: The problem with this one is that you can focus on the
            IMEI number and also that it has 2 buttons: Ok and Back.
        """

        # Not nice - truncated
        # Old: appuifw.popup_menu([u" "],
        #    unicode("Your phone ID (and IMEI number) is " + deviceId + "."))

        # Old: appuifw.popup_menu([u"(and IMEI number) is",
        #    unicode(deviceId + ".")], unicode("Your phone ID"))

        #appuifw.popup_menu([unicode(deviceId + ".")],
        #    unicode("Your device ID (IMEI):"))

        """
        Maybe see "Using AVKON UI Notes API" -
            see http://www.forum.nokia.com/document/Cpp_Developers_Library/GUID-759FBC7F-5384-4487-8457-A8D4B76F6AA6/html/Notes_API4.html
        """

        """
        The problem with this one is that you can focus on the IMEI number
            and also that it has 2 buttons: Ok and Back.
        See a list of various UI dialogs here:
            http://www.mobilenin.com/pys60/info_dialogue_notes.htm .
        """
        #appuifw.note(unicode("Your device ID (IMEI) is " +
        #    deviceId + "."), "info")

        # Doesn't help the global flag either.
        #appuifw.note(unicode("Your device ID (IMEI): " +
        #    deviceId + "."), "conf", 1)

        # Not good: it fills up the entire screen and it truncates the text,
        #     being too long.
        #appuifw.selection_list([unicode("Your phone ID (and IMEI number) is " +
        #    deviceId + ".")], 0)

        try:
            myDroid.makeToast(aText)

            # It appears the Toast notification takes invariably 2 seconds.
            time.sleep(waitTime)
        except:
            DebugPrintErrorTrace()


def DialogMultipleChoices(title, comboList, initialChoice):
    if ANDROID_OS:
        try:
            myDroid.dialogCreateAlert(title)

            # See http://www.mithril.com.au/android/doc/UiFacade.html#dialogSetSingleChoiceItems
            myDroid.dialogSetSingleChoiceItems(comboList, int(initialChoice))

            myDroid.dialogSetPositiveButtonText("OK")

            myDroid.dialogShow()

            #DisplayNote(str(myDroid.dialogGetResponse()))
            """
            It displays, I think, {u'which': u'positive'} - probably this is
                the result of pressing the OK button.
            """

            # See http://www.mithril.com.au/android/doc/UiFacade.html#dialogGetSelectedItems
            #DisplayNote(str(myDroid.dialogGetSelectedItems()))
            """
            myDroid.dialogGetSelectedItems() returns, for example:
                Result(id=81 , result=[1], error=None)
            """

            DebugPrint("DialogMultipleChoices(): " \
                    "myDroid.dialogGetResponse() = %s\n" % \
                                            str(myDroid.dialogGetResponse()) +
                    "DialogMultipleChoices(): " \
                        "myDroid.dialogGetSelectedItems() = %s" % \
                                        str(myDroid.dialogGetSelectedItems()))

            # !!!!TODO: think if necessary - Don't allow reading events:
            #myAllowReadEvents = False

            res = int(myDroid.dialogGetSelectedItems().result[0])
            #Once I got here: TypeError: int() argument must be a string or a number, not 'dict'

            return res
        except:
            DebugPrintErrorTrace()
            return 0


def DialogGetInput(aTitle, aTextMore, aInitVal):
    global myAllowReadEvents

    if SYMBIAN_OS:
        resStr = appuifw.query(unicode(aTitle) + aTextMore, "text",
                               unicode(aInitVal))  # "number", "code", etc
    elif ANDROID_OS:
        try:
            # Don't allow reading events:
            myAllowReadEvents = False

            resStr = myDroid.dialogGetInput(aTitle, aTextMore, aInitVal).result

            if (resStr is None) or (resStr == []):
                resStr = ""

            DebugPrint("DialogGetInput(): resStr = %s." % resStr)

            myAllowReadEvents = True
        except:
            DebugPrintErrorTrace()

    return resStr


if iOS_PYOBJC:
    import subprocess


def GetSerialAndRevision_Raspbian():
    """
    Inspired from
        https://raspberrypi.stackexchange.com/questions/2086/how-do-i-get-the-serial-number

    From http://elinux.org/RPi_HardwareHistory - for revision number
        cat /proc/cpuinfo
    """
    # Extract serial from cpuinfo file
    cpuserial = "RPi" #"0000000000000000"
    try:
        f = open("/proc/cpuinfo", "r")
        for line in f:
            if line[0:6] == "Serial":
                cpuserial += line[10:26]
                break
        f.close()
    except:
        cpuserial = "ERROR000000000"

    DebugPrint("GetSerialAndRevision_Raspbian(): cpuserial = %s" % cpuserial)
    return cpuserial

def GetDeviceId_iPhone():
    """
    This is a named tuple - see
        http://docs.python.org/library/collections.html#collections.namedtuple
    """
    # TypeError: tuple indices must be integers, not str
    #print "deviceId =", deviceId["result"]

    # Result(id=1, result=u'00...', error=None)
    #print "deviceId =", deviceId

    #print "deviceId[1] =", deviceId[1]
    #print "deviceId[0] =", deviceId[0]
    #print "deviceId =", deviceId.result
    try:
        DebugPrint("GetDeviceId(): calling subprocess.call().")
        """
        See http://stackoverflow.com/questions/6441807/spawn-a-new-non-blocking-process-using-python-on-mac-os-x
            (also http://stackoverflow.com/questions/2260614/how-to-switch-to-a-python-subprocess-created-by-ipython-on-os-x)
        """
        # Nothing happens.
        # subprocess.call(["/Applications/pyExamp.app/pyExamp", "", ""])
        #       , stdout = None, stderr = fOutput)

        # subprocess.call(["/Applications/pyExamp.app/pyExamp"])
        #    , stdout = None, stderr = fOutput)

        # os.system("/Applications/pyExamp.app/pyExamp")

        # fOutput = open("/var/mobile/iCam/IMEI.txt", "wb")
        fOutput = open(LOCAL_FOLDER + "/IMEI.txt", "wb")

        #subprocess.call(["/var/14/deviceinfo", "-i"], stdout = fOutput,
        #                stderr = None) #fOutput)

        #subprocess.call([LOCAL_FOLDER + "/deviceinfo", "-i"],
        #                stdout = fOutput, stderr = None)
        subprocess.call([sys.path[0] + "/deviceinfo", "-i"],
                        stdout=fOutput, stderr=None)
        fOutput.close()
    except:
        traceback.print_exc()
        sys.stderr.flush()

    # threading.Timer(0.1, ExecProcess).start()
    # thread.start_new_thread(ExecProcess, ())
    # ExecProcess()

    try:
        fInput = open(LOCAL_FOLDER + "/IMEI.txt", "r")
        myRes = fInput.read()
        fInput.close()

        #DebugPrint("GetDeviceId(): (before) myRes = %s" % myRes)

        myRes = myRes.rstrip(" \r\n")
        #DebugPrint("GetDeviceId(): (after) myRes = %s" % myRes)
    except:
        DebugPrintErrorTrace()
        myRes = IMEI_IOS

    return myRes


def GetDeviceId():
    """
    Returns the deviceId of the running device.

    !!IMPORTANT: deviceId should not have '_' chars in it, because I am using
        '_' as separator char in filenames containing deviceId also.
    deviceId has normally 15-17 chars (e.g., "N95N95N95N95N95", "35598001238745601")
    """

    # if SYMBIAN_OS:
    if SYMBIAN_S60_OS:
        # sysinfo.imei() returns unicode string, so we convert to ASCII string.
        return str(sysinfo.imei())
    elif SYMBIAN_UIQ_OS:
        return IMEI_UIQOS
    elif ANDROID_OS:
        # From PhoneFacade
        return str(myDroid.getDeviceId().result)
    elif iOS_PYOBJC:
        return GetDeviceId_iPhone()
    elif WINDOWS_OS:
        return IMEI_WinOS #Note that this has 14 chars lenght, not 15
    elif WINDOWS_CE_OS_PYTHONCE:
        #WinSpawn(r"\Storage Card\iCam_WinMobile\GetIMEI.exe", [])

        # We use \ instead of /
        #WinSpawn(r"\Storage Card\iCam\GetIMEI.exe", [])
        tmpPathFileName = LOCAL_FOLDER + "/GetIMEI.exe"

        # I think we require backslashes.
        tmpPathFileNameWithBackslashes = tmpPathFileName.replace("/", "\\")

        WinSpawn(tmpPathFileNameWithBackslashes, [])

        time.sleep(5.0)

        try:
            # fInput = open(r"\Storage Card\iCam\IMEI.txt", "r")
            fInput = open(LOCAL_FOLDER + "/IMEI.txt", "r")
            myRes = fInput.read()
            fInput.close()
        except:
            DebugPrintErrorTrace()

            myRes = IMEI_WinCEOS

        return myRes
    elif UNIX_OS:
        return IMEI_UNIXOS
    elif RASPBIAN_OS:
        #!!!!TODO: use hostname
        #!!!!!!!!TODO: use GetSerialAndRevision_Raspbian()
        myRes = GetSerialAndRevision_Raspbian()
        return str("RPi")

###############################################################################
###############################################################################
###############################################################################
################MOST OF THE STATE VARIABLES START FROM HERE####################
#####################(BUT SOME ARE ALSO DEFINED ABOVE)#########################
###############################################################################


# Note that GetDeviceId() returns an ASCII string.
if ANDROID_OS and ANDROID_OS_QPYTHON:
    deviceId = "ANDROID_OS_QPYTHON_phone"
    # !!!!TODO: QPython, because it doesn't have permissions, GetDeviceId() gives
    #    "java.lang.SecurityException: Requires READ_PHONE_STATE: Neither user 10156 nor current process has android.permission.READ_PHONE_STATE."
else:
    deviceId = GetDeviceId()

"""
localPhotoResolution = None
localVideoMode = None
"""

localPhotoResolution = [(0, 0), (0, 0)] # [(width, height), ...]
localVideoMode = [(0, 0), (0, 0)] # Example: localVideoMode = [((176, 144), 15.0), ((176, 144), 15.0)]

# modeManagerIsEnabled = True
modeManagerIsEnabled = False

burstModeIsStarted = False

"""
Burst mode take as many frames as possible per second when the viewfinder is
    on. This happens when pauseInterval == 0.
"""
motionDetectionIsOn = False
# Detect simple changes in images.
#motionDetectionIsOn = True

# Simply detect noise
#noiseDetectionEnabled = True

# faceDetection, body tracking
# soundDetection #a human talking, a dog barking, a bird singing

"""
IMPORTANT NOTE: In landscape mode, N82 has a resolution of (320, 199) -
    surprisingly it seems to be 199, not 200. The rest of 41 pixels is for
    the system status.

#This is conserving aspect ratio for (320, 240), as for (176, 144)
# VIEWFINDER_SIZE_ORIG = (293, 240)
"""
if SYMBIAN_OS:
    if SYMBIAN_3:
        VIEWFINDER_SIZE_ORIG = (293, 240)
    else:
        # VIEWFINDER_SIZE_ORIG = (73, 60) #Bad
        VIEWFINDER_SIZE_ORIG = (293, 240)  # Good
else:
    # VIEWFINDER_SIZE_ORIG = (80, 60) #Bad
    # VIEWFINDER_SIZE_ORIG = (176, 144) #Bad

    # This is conserving aspect ratio for (240, 180), as for (176, 144)
    # VIEWFINDER_SIZE_ORIG = (220, 180)
    VIEWFINDER_SIZE_ORIG = (-1, -1)
# SIZE_VIEWFINDER = (320, 240)
# SIZE_VIEWFINDER = (512, 384)
# SIZE_VIEWFINDER = (640, 480)
viewFinderSize = VIEWFINDER_SIZE_ORIG

readGPS = 0  # False
gpsInfo = {"position": {}, "course": {}, "satellites": {}}

# We assume 2 cameras by default.
numCamerasSupported = 2

"""
The default configuration for the application.
pauseInterval represents the interval of time the system waits between media
    capture actions (video recordings, take photos).
NOT USED: pauseInterval represents the interval of time the system waits in a
    Reactive loop.
"""
PAUSE_INTERVAL_POWER_MANAGED = 10 * 60 * 60  # 10 hours
pauseInterval = 120  # 2 minutes
"""
YouTube (and Picasa?) might give exceptions if uploading too often videos,
    delete, create or delete new playlists --> we wait between such
    consecutive operations.
"""
pauseIntervalGdata = 60

digitalZoom = 0  # no zoom
photoResolutionIndex = 6  # 320x240
photoModeIndex = [3, 2]  # JPEG_Exif, RGB24
photoQuality = 50  # 50% quality
exposureIndex = [0] * 2
whiteBalanceIndex = [0] * 2
flashIndex = 1
audioRecordDuration = 0
videoRecordDuration = [30, 30]  # [0, 0] #[0, 7]
rotateDegreesImage = 0
localQualityIndex = 0
localPhotoResolutionIndex = [None, None]

# 0 = None, 1 = Only Video, 2 = Only Photo, 3 = Both Photo and Video
cameraMode = [1, 0]
if RASPBIAN_OS: #!!!!TODO: doesn't work well - try more
    cameraMode = [2, 0]


# Currently cannot record video on iOS
if iOS_PYOBJC:
    cameraMode = [2, 0]

videoAudioEnabled = 1  # True #False
localVideoModeIndex = [0, 0]  # [None, None]

"""
0 = no local storage;
1 = store all media (on SD card);
in the future, we should have other values as well
    (e.g., 2 = store only movies, but not photos)
"""
storeLocallyMedia = 1

"""
orientationForThisPhoneModel on Symbian has to do with the orientation of the
    camera. It can be "portrait" (normal) or "landscape".
"""
orientationForThisPhoneModel = "portrait" #"landscape"

mobileCountryCode = -1
mobileNetworkCode = -1
locationAreaCode = -1
cellId = -1

phoneModel = ""

# We can use lookup after deviceId to get the phoneNumber
# phoneNumber = "no_phone_number" + phoneModel
phoneNumber = "no_phone_number"

# 0 is None; 1 is BT server; 2 is BT client
bluetoothMode = 0

bluetoothServerAddress = ""
bluetoothSelfAddress = "00:00:00:00:00:00" # ""
bluetoothSelfName = ""

cameraPhotoSizes_JPEG_Exif = [None, None]
cameraPhotoSizes_RGB24 = [None, None]

"""
cameraVideoFormats = [None, None]
cameraVideoFrameSizes = [None, None]
cameraVideoModes = [None, None]
"""
cameraVideoFormats = [[], []] # Ex: cameraVideoFormats[1] = ['EFormatYUV420Planar']
cameraVideoFrameSizes = [[], []] # Ex: cameraVideoFrameSizes[1] = [(640, 480), (352, 288), (320, 240), (176, 144)]
cameraVideoModes = [[], []] # Ex: cameraVideoModes[1] = [{"rate": 15.0, "size": (176, 144)}]

myMaxRamdriveSize = -1

differentPixelsPercentage = 10
differentPixelsPercentageThreshold = [-1, -1]

stateTime = None
dawnTimeVec = [-1, -1, -1]
duskTimeVec = [-1, -1, -1]

"""
When the phone receives internetUploadMaxErrors upload errors we Quit iCam.
  This is extremely useful for SYMBIAN_OS, where we programatically connect
    to the AP when iCam starts, so restarting iCam is certain to reconnect
    to the AP.

The value should be at least specific per type of
    mobile OS - to be set accordingly.
"""
internetUploadMaxErrors = 20 #100

"""
Used for InternetUploadBinaryData() and YouTubeVideoUpload().
TODO:!!!! use it for PicasaPhotoUpload(), as well.
"""
internetUploadErrorsCounter = 0


# Upload all BT messages.
# Upload that many videos in reverse chronological order:
uploadHowManyOfLatestBluetoothMessages = 4 # 5
# Upload that many videos in chronological order - not really great if there are many:
#uploadHowManyOfLatestBluetoothMessages = -1

###############################################################################
###############################################################################
###############################################################################
###########################END STATE VARIABLES#################################
###############################################################################
###############################################################################
"""
Have to declare atom and gdata global in order to import the modules in the
    global scope in ImportGdataModules().
"""
atom = None
gdata = None

gdataModulesImported = False


"""
In PyS60 I cannot bypass the standard library module loading.
Hence we need to load the module ourselves, before any other (sub)module.
Once we load the module M explicitly, it is in the program modules dictionary
    and any subsequent module that loads the module M will refer to this module
    and will not load again the module.
Note that although the import gets lost when leaving ImportMyModule() since it is
    local, it seems that the operations performed are enough in order to load the
    second time the module from the same path as here.
"""

"""
# Just for testing purposes
array = None
def ImportArray():
    if WINDOWS_OS:
        if sys.version_info[0 : 2] != (2, 2):
            return
    else:
        return

    try:
        import imp

        DebugPrint(str(imp.find_module("array"))

        #modulePathFileName = "C:\\Python22_S60\Lib\\urlparse.py"

        if True: #False:
            modulePathFileName = "C:\\Python22\\Lib\\site-packages\\array.py"
            fInput = open(modulePathFileName, "rb")
            global array
            array = imp.load_module("array", fInput, modulePathFileName, ('.py', 'r', 1))

            DebugPrint("Alex: %s" % str(array))
    except:
        DebugPrintErrorTrace()
ImportArray()
"""


#urlparse = None
def ImportMyModule():
    try:
        import imp

        #DebugPrint(str(imp.find_module("urlparse"))

        if False:
            modulePathFileName = "C:\\Python22_S60\\Lib\\site-packages\\urlparse.py"
            fInput = open(modulePathFileName, "rb")
            res = imp.load_module("urlparse", fInput, modulePathFileName, ('.py', 'r', 1))

            #DebugPrint("Alex: %s" % str(res))

        modulePathFileName = "E:\\Private\\e21e55e0\\site-packages\\urlparse.pyc"
        fInput = open(modulePathFileName, "rb")
        res = imp.load_compiled("urlparse", modulePathFileName, fInput)

        #DebugPrint("Alex: %s" % str(res))
    except:
        DebugPrintErrorTrace()


def ImportGdataModules():
    global atom, gdata
    global gdataModulesImported

    DebugPrint("Entered ImportGdataModules(): gdataModulesImported = %d." % \
            gdataModulesImported)

    if gdataModulesImported == False:
        try:
            if gdata is None:
                import gdata

            #if sys.version_info[0 : 2] == (2, 2):
            if SYMBIAN_OS:
                if not _PyS60_1_9_OR_NEWER:
                    ImportMyModule()

            #"""
            try:
                import gdata.tlslite
            except:
                DebugPrintErrorTrace()

            try:
                import gdata.tlslite.utils
            except:
                DebugPrintErrorTrace()

            try:
                import gdata.tlslite.utils.keyfactory
            except:
                DebugPrintErrorTrace()
            #"""

            if uploadMediaToYouTube or uploadMediaToPicasa:
                # if atom is None:
                import atom
                """
                # As suggested in http://effbot.org/zone/import-confusion.htm,
                #    I could give:
                atom = __import__("atom")
                # but this doesn't work well for "gdata.youtube" :) - it gives
                # error when loading module gdata.youtube because it is written
                # trickier... :)
                """
                # See maybe http://docs.python.org/release/2.5.2/ref/import.html

                import gdata.media

                if uploadMediaToYouTube:
                    import gdata.youtube
                    import gdata.youtube.service
                if uploadMediaToPicasa:
                    import gdata.photos
                    # Gives MemoryError if I use default.py directly
                    import gdata.photos.service

            import gdata.tlslite.utils.Python_AES

            gdataModulesImported = True
        except:
            gdataModulesImported = False
            DebugPrint("Not able to import the gdata (and atom) modules.")
            DebugPrintErrorTrace()


# ImportGdataModules()

"""
We try to be "lazy" (as in lazy evaluation) in importing the gdata modules -
    we want to import them ALAP.
"""


"""
googleUsername = "MOBILEREVIVAL"

# Note that googleKeywords could also store location, phone name, etc:
googleKeywords = "Cernica N82"
"""
googleUsername = None
googlePassword = None
googlePasswordEncrypted = None
googleKeywords = deviceId

"""
This is the keyword that we add for all devices in the BT network.
    TODO!!!!: The keyword should be added through UI at the BT server.
"""
btNetSearchKeywords = "Cernica" #N82"
"""
if ANDROID_OS:
    #googleKeywords = GetPhoneModel() #"Android device"
elif SYMBIAN_OS:
    #googleUsername = "ender..."
    #googleKeywords = "E7"
    googleKeywords = deviceId
"""

"""
uploadMediaToYouTube = 0
uploadMediaToPicasa = 0
useiCamServer = 2
"""
# if gdataModulesImported:

"""
# OLD meaning: useiCamServer = 0 # 0 = just state, 1 = all
useiCamServer:
    0 = no (none),
    1 = no Media upload (state, log, download updates and commands),
    2 = all (upload State + Media, download updates and commands)
"""
useiCamServer = None

if (SYMBIAN_OS and S60_EDITION[0] >= 3) or ANDROID_OS or iOS_PYOBJC:
    uploadMediaToYouTube = 1
    uploadMediaToPicasa = 1
    useiCamServer = 0 # 1

    if deviceId == IMEI_E7:
        uploadMediaToIQEngines = 0  # 1
    else:
        uploadMediaToIQEngines = 0
else:
    """
    On WINDOWS_CE_OS_PYTHONCE I don't have (yet) OpenSSL so gdata complains
        (ssl module not found or so) and I can't login to Google gdata servers.
    On WINDOWS_OS I normally prefer to test intranet performance with the iCam
        server running on the Linux VMWare box.
    """
    uploadMediaToYouTube = 0
    uploadMediaToPicasa = 0
    useiCamServer = 2
    uploadMediaToIQEngines = 0
googleRememberPassword = 1
googleMediaPrivate = 1

if WINDOWS_CE_OS_PYTHONCE:
    ImportGdataModules()
    googleUsername = ""
    # For test only.

    """
    Yes, we use one space because StoreLocalConfigInFile() requires at least 1
        char to generate an encrypted password. :)
    """
    googlePassword = " "
    googlePasswordEncrypted = ""
    # StoreLocalConfigInFile()

###############################################################################
###############################################################################
###############################################################################
###########################END CONFIG VARIABLES################################
###############################################################################
###############################################################################

###############################################################################
###############################################################################
#######################BEGIN STATE LOAD/STORE FUNCTIONS########################
###############################################################################
###############################################################################

import struct


"""
!!!!IMPORTANT: do not change easily deviceIdFormat and statePackFormat*,
    since we use them in quite a few places.
"""
deviceIdFormat = "<100s"

# See http://docs.python.org/2/library/struct .
# Note: <i is 32 bits long integers, in little endian format.
"""
Format      C Type              Python type      Standard size       Notes
x           pad byte            no value         
c           char                string of length    1                   1    
b           signed char         integer             1                 (3)
B           unsigned char       integer             1                 (3)
?           _Bool               bool                1                 (1)
h           short               integer             2                 (3)
H           unsigned short      integer             2                 (3)
i           int                 integer             4                 (3)
I           unsigned int        integer             4                 (3)
l           long                integer             4                 (3)
L           unsigned long       integer             4                 (3)
q           long long           integer             8            (2), (3)
Q           unsigned long long  integer             8            (2), (3)
f           float               float               4                 (4)
d           double              float               8                 (4)
s           char[]              string       
p           char[]              string       
P           void *              integer                          (5), (3)
""" 

# statePackFormat  = "<iiiiiiii"

# sizeStateMarshalled
statePackFormat00 = "<i"

"""
bbbB
uploadMediaToYouTube, uploadMediaToPicasa, useiCamServer,
internetUploadMaxErrors, 

ibbbb
BATTERY_LEVEL_THRESHOLD,
reactiveLoopOpsIndex, sentBTMessageTo6680, 0, 0,

ii
mediaFileSize, 0,
i
uploadHowManyOfLatestBluetoothMessages,

bbb
modeManagerIsEnabled, startAutomatically, saveUnsentPackets,

b
uploadUnsentData,

bbbb
MY_DEBUG_STDOUT, MY_DEBUG_STDERR, MY_DEBUG_STDERR_2, MY_DEBUG_UPLOAD_MSG
"""
statePackFormat01 = "<bbbBibbbbiiibbbbbbbb"

"""
!!!!IMPORTANT: do not change easily statePackFormat02 since we use it in
    GetInfoFromSMFBtMsg(), the least.
"""
# accessPointName, MAC bluetoothSelfAddress (the 6 bytes), tmpBluetoothServerAddress, cameraId
statePackFormat02 = "<44sBBBBBB20si"

# crtTime.tm_year, crtTime.tm_mon, crtTime.tm_mday, crtTime.tm_hour,
#   crtTime.tm_min, crtTime.tm_sec, numMilliseconds
#statePackFormat03 = "<iiiiiii"
statePackFormat03 = "<iibbbbi"
#int(GetTime()), 0, 0
statePackFormat03b = "<iii"

# GetBatteryLevelPercentage(), main_drive_free_space, memory_card_free_space,
#   GetFreeRAM(), 0, sysagent.charger_status()
statePackFormat04 = "<iqqiii"

# The 2s is for the signalUnits. signalStrength
statePackFormat05 = "<2si"

# pauseIntervalGdata,pauseInterval,burstModeIsStarted,
#   photoResolutionIndex, 0, localPhotoResolutionIndex[0],
#   localPhotoResolutionIndex[1], photoModeIndex[0]
statePackFormat06 = "<iiiiiiii"

# photoModeIndex[1], digitalZoom, 0, photoQuality, 0, exposureIndex[0],
#   exposureIndex[1]
statePackFormat07 = "<iiiiiii"

# whiteBalanceIndex[0], whiteBalanceIndex[1], flashIndex, 0
statePackFormat08 = "<iiii"

# 0, 0, 0, 0, 0, 0, 0, 0. Indices for:
# ExposureCompensation, ISO, contrast, sharpness, color tone, Scene Modes
statePackFormat09 = "<32s32s32s32s"
# Reserved (ex: ShutterSpeed)
# Reserved: indices for localVideoMode (VideoFrameRates, VideoEncoding),
#   Video Stabilization, Audio Recording, etc
# (Note: Camera app on S60 on N82 has presets: TV High Quality,
#   TV Normal Quality, Email High Quality, Email Normal Quality,
#   Sharing Quality)
# Reserved (ex: sent videoResolution)

# videoRecordDuration[0], videoRecordDuration[1], localVideoModeIndex[0],
#   localVideoModeIndex[1], cameraMode[0], cameraMode[1], videoAudioEnabled,
#   0 (maybe videoAudioEnabled camera with id 1),
statePackFormat10 = "<iiiibbbb"

# Reserved (ex: audioType - MP3 or AMR)
statePackFormat11 = "<20s"

# audioRecordDuration, 0, 0, 0, 0, 0, 0 (reserved for other durations)
statePackFormat12 = "<i24s"

# rotateDegreesImage, the rest is reserved
statePackFormat13 = "<i28s"

# mobileCountryCode, mobileNetworkCode, locationAreaCode, cellId
statePackFormat14 = "<iiii"

# Reserved
statePackFormat15 = "<32s"

# storeLocallyMedia. And reserved for "Choose when to send Unsent", plugged
#   and charging or not
statePackFormat16 = "<i28s"

# motionDetectionIsOn, differentPixelsPercentageThreshold[0];
# Reserved for noiseDetectionEnabled, faceDetection,
#   soundDetection (a human talking, a dog barking, a bird singing)
statePackFormat17 = "<if24s"

# Reserved for motion detection params (thresholds, etc)
statePackFormat18 = "<32s32s32s32s"
# Reserved for the result(s) returned by the simple detection algorithm(s).
# Reserved for the result(s) returned by the simple detection algorithm(s).
# Reserved for the result(s) returned by the simple detection algorithm(s).

# Reserved (ex: readGPS)
statePackFormat19 = "<32s"

# GPS data (gpsInfo)
statePackFormat20 = "<dddddddddddddii"

# Reserved for gps related.
statePackFormat21 = "<32s32s"

# logAccelerometerAndRotationSensors, and reserved
statePackFormat22 = "<i28s"

# 14 * 32 bytes (strings) reserved. fileName
# statePackFormat23 = "<32s32s32s32s32s32s32s32s32s32s32s32s32s32s256s"
# 13 * 32 bytes (strings) reserved.
statePackFormat23 = "<32s32s32s32s32s32s32s32s32s32s32s32s32s"

# Reserved dawnTimeVec[0], dawnTimeVec[1], dawnTimeVec[2], 0, duskTimeVec[0],
#   duskTimeVec[1], duskTimeVec[2], 0
statePackFormat24 = "<20sbbbbbbbb"

statePackFormat25 = "<i" # No value: 0
statePackFormat26 = "<128s" # fileName
statePackFormat27 = "<124s" # ICAM_SERVER_NAME
statePackFormat28 = "<i" # CRC32 of the state

statePackFormat = statePackFormat00 + statePackFormat01[1:] + \
    statePackFormat02[1:] + statePackFormat03[1:] + statePackFormat03b[1:] + \
    statePackFormat04[1:]
statePackFormat += statePackFormat05[1:] + statePackFormat06[1:] + \
    statePackFormat07[1:] + statePackFormat08[1:] + \
    statePackFormat09[1:]
statePackFormat += statePackFormat10[1:] + statePackFormat11[1:] + \
    statePackFormat12[1:] + statePackFormat13[1:] + \
    statePackFormat14[1:]
statePackFormat += statePackFormat15[1:] + statePackFormat16[1:] + \
    statePackFormat17[1:] + statePackFormat18[1:] + \
    statePackFormat19[1:]
statePackFormat += statePackFormat20[1:] + statePackFormat21[1:] + \
    statePackFormat22[1:] + statePackFormat23[1:] + \
    statePackFormat24[1:]
statePackFormat += statePackFormat25[1:] + statePackFormat26[1:] + \
    statePackFormat27[1:] + statePackFormat28[1:]



def LoadStateFromFile(pathFileName, myDebugStdout=MY_DEBUG_STDOUT, \
                                            myDebugStderr=MY_DEBUG_STDERR):

    global burstModeIsStarted, photoResolutionIndex, photoQuality, \
        pauseInterval, pauseIntervalGdata, digitalZoom
    global localPhotoResolutionIndex, photoModeIndex, exposureIndex, \
        whiteBalanceIndex, flashIndex
    global startViewfinderBeforeTakingPhoto
    global audioRecordDuration, videoRecordDuration
    global localVideoModeIndex, cameraMode, videoAudioEnabled
    global accessPointName, bluetoothServerAddress
    global motionDetectionIsOn, differentPixelsPercentageThreshold
    global rotateDegreesImage
    global statePackFormat
    global storeLocallyMedia
    global modeManagerIsEnabled, dawnTimeVec, duskTimeVec
    global startAutomatically
    global BATTERY_LEVEL_THRESHOLD
    global reactiveLoopOpsIndex
    global uploadMediaToYouTube, uploadMediaToPicasa, useiCamServer
    global MY_DEBUG_STDOUT, MY_DEBUG_STDERR, MY_DEBUG_STDERR_2, \
        MY_DEBUG_UPLOAD_MSG
    global ICAM_SERVER_NAME
    global saveUnsentPackets, uploadUnsentData, internetUploadMaxErrors, \
        uploadHowManyOfLatestBluetoothMessages
    global sentBTMessageTo6680
    global stateTime

    if myDebugStdout:
        print "Entered LoadStateFromFile(pathFileName = %s)." % pathFileName
        sys.stdout.flush()

    try:
        """
        if myDebugStdout:
            print "os.path.isfile(%s) = %s." % (pathFileName,
                    os.path.isfile(pathFileName))
            sys.stdout.flush()
        """

        # pathFileNameBackup = STATE_PATH_FILENAME_BACKUP
        pathFileNameBackup = pathFileName + STATE_BACKUP_EXTENSION
        if not os.path.isfile(pathFileName):
            if myDebugStdout:
                print "The state file %s doesn't exist." % pathFileName
                sys.stdout.flush()

            if os.path.isfile(pathFileNameBackup):
                if myDebugStdout:
                    print "We found the backup of the state file (%s) --> " \
                            "we restore it as the original state file." % \
                            pathFileNameBackup
                    sys.stdout.flush()
                MoveFileBetweenAnyDrives(pathFileNameBackup, pathFileName)
            else:
                # pathFileName = pathFileNameBackup
                return False

        try:
            fInput = open(pathFileName, "rb")
            fileData = fInput.read()
            fInput.close()

            fileData = fileData.decode("zlib")
        except:
            """
            There was an error reading pathFileName (it happened once that
                state.bin was 0 bytes)
            """
            if myDebugStderr:
                traceback.print_exc()
                sys.stderr.flush()

            try:
                if os.path.isfile(pathFileNameBackup):
                    if myDebugStdout:
                        print "The state file %s is invalid. We found the " \
                                "backup of the state file (%s) --> we restore"\
                                " it as the original state file." % \
                                (pathFileName, pathFileNameBackup)
                        sys.stdout.flush()

                    os.unlink(pathFileName)
                    MoveFileBetweenAnyDrives(pathFileNameBackup, pathFileName)
                    # pathFileName = pathFileNameBackup

                    fInput = open(pathFileName, "rb")
                    fileData = fInput.read()
                    fInput.close()
                    fileData = fileData.decode("zlib")
                else:
                    return False
            except:
                if myDebugStderr:
                    traceback.print_exc()
                    sys.stderr.flush()
                return False

        stateSize = struct.calcsize(statePackFormat)

        (crc32, ) = struct.unpack(statePackFormat28, \
                            fileData[stateSize - 4 : stateSize])
        crtCrc32 = binascii.crc32(fileData[0 : stateSize - 4])

        if myDebugStdout:
            print "LoadStateFromFile(): crtCrc32 = 0x%X, crc32 = 0x%X" % \
                                                        (crtCrc32, crc32)
        if (crtCrc32 != crc32) or (crc32 == 0):
            if myDebugStdout:
                print "LoadStateFromFile(): CRC error: " \
                        "crtCrc32 = 0x%X, crc32 = 0x%X --> bailing out." % \
                        (crtCrc32, crc32)
            return False

        """
        Note: uploadMediaToYouTube, uploadMediaToPicasa,
           useiCamServer are stored and loaded from
           LOCAL_CONFIG_PATH_FILENAME

        uploadMediaToYouTube, uploadMediaToPicasa, useiCamServer,
           _, _, _, _, _, uploadHowManyOfLatestBluetoothMessages, _,
            startAutomatically, modeManagerIsEnabled, uploadUnsentData,
            MY_DEBUG_STDOUT, MY_DEBUG_STDERR, MY_DEBUG_STDERR_2,
            MY_DEBUG_UPLOAD_MSG,

        _, _, _, _, BATTERY_LEVEL_THRESHOLD, _, internetUploadMaxErrors, _,
            uploadHowManyOfLatestBluetoothMessages, modeManagerIsEnabled,
            startAutomatically, saveUnsentPackets, uploadUnsentData,
            MY_DEBUG_STDOUT, MY_DEBUG_STDERR, MY_DEBUG_STDERR_2,
            MY_DEBUG_UPLOAD_MSG,
        """
        (sizeStateMarshalled,
            # IMPORTANT: the first three wildcards represent the
            # uploadMediaToYouTube, uploadMediaToPicasa, useiCamServer.
            # Altough they are being stored in both the state and config data,
            #    they are reloaded only from the config file iCam.cfg.
            _, _, _, internetUploadMaxErrors, BATTERY_LEVEL_THRESHOLD,
            reactiveLoopOpsIndex, sentBTMessageTo6680, _, _,
            _, _, # mediaFileSize, ...
            uploadHowManyOfLatestBluetoothMessages, modeManagerIsEnabled,
            startAutomatically, saveUnsentPackets, uploadUnsentData,
            MY_DEBUG_STDOUT, MY_DEBUG_STDERR, MY_DEBUG_STDERR_2,
            MY_DEBUG_UPLOAD_MSG,

            accessPointName, _, _, _, _, _, _, # bluetoothSelfAddress in 6 bytes
                bluetoothServerAddress, _, # cameraId,

            # crtTime.tm_year, crtTime.tm_mon, crtTime.tm_mday,
            _, _, _,
            # crtTime.tm_hour, crtTime.tm_min, crtTime.tm_sec, numMilliseconds
            _, _, _, _,

            # int(GetTime()), 0, 0,
            stateTime, _, _,

            # GetBatteryLevelPercentage(), main_drive_free_space,
            #   memory_card_free_space, GetFreeRAM(), 0, charger_status,
            _, _, _, _, _, _,

            # signalUnits, signalStrength,
            _, _,

            pauseIntervalGdata, pauseInterval,

            # burstModeIsStarted, photoResolutionIndex, _, ...
            _, photoResolutionIndex, _, localPhotoResolutionIndex[0],
            localPhotoResolutionIndex[1], photoModeIndex[0],

            # First _ is for opticalZoom
            photoModeIndex[1], digitalZoom, _, photoQuality, _,

            exposureIndex[0], exposureIndex[1],
            whiteBalanceIndex[0], whiteBalanceIndex[1], flashIndex,
            startViewfinderBeforeTakingPhoto,

            #"", (0, 0, 0, 0, 0, 0, 0, 0,) - Indices for:
            #   ExposureCompensation, ISO, contrast, sharpness, color tone,
            #   Scene Modes
            _,

            #"", #0, 0, 0, 0, 0, 0, 0, 0, - Reserved (ex: ShutterSpeed)
            _,

            #"", #0, 0, 0, 0, 0, 0, 0, 0, - Indices for
            #   localVideoMode (VideoFrameRates, VideoEncoding),
            #   Video Stabilization, Audio Recording, etc
            #   (Note: Camera app on S60 on N82 has presets: TV High Quality,
            #   TV Normal Quality, Email High Quality, Email Normal Quality,
            #   Sharing Quality)
            _,

            #"", 0, 0, 0, 0, 0, 0, 0, 0, - Reserved (ex: sent videoResolution)
            _,

            videoRecordDuration[0], videoRecordDuration[1],
            localVideoModeIndex[0], localVideoModeIndex[1],
            cameraMode[0], cameraMode[1], videoAudioEnabled, _,

            #"" (0, 0, 0, 0, 0, 0, 0, 0,) - Reserved
            #   (ex: audioType - MP3 or AMR)
            _,

            #"" (0, 0, 0, 0, 0, 0,) - Reserved for other durations
            audioRecordDuration, _,

            # rotateDegreesImage, "" (0, 0, 0, 0, 0, 0, 0,)
            rotateDegreesImage, _,

            # mobileCountryCode, mobileNetworkCode, locationAreaCode, cellId,
            _, _, _, _,

            #"" (0, 0, 0, 0, 0, 0, 0, 0,) - reserved
            _,

            #storeLocallyMedia, "", (0, 0, 0, 0, 0, 0, 0)
            #   Reserved for choose when to send Unsent, plugged and charging
            #   or not
            storeLocallyMedia, _,

            # "" (0, 0, 0, 0, 0, 0, 0,) - Reserved for noiseDetectionEnabled,
            #   faceDetection, soundDetection (a human talking, a dog barking,
            #   a bird singing)
            motionDetectionIsOn, differentPixelsPercentageThreshold[0], _,

            #"" (0, 0, 0, 0, 0, 0, 0, 0,) - Reserved for motion detection
            #   params (thresholds, etc)
            _,

            #"" (0, 0, 0, 0, 0, 0, 0, 0,) - Reserved for the result(s)
            #   returned by the simple detection algorithm(s)
            _,

            #"" (0, 0, 0, 0, 0, 0, 0, 0,) - Reserved for the result(s)
            #   returned by the simple detection algorithm(s)
            _,

            #"" (0, 0, 0, 0, 0, 0, 0, 0,) - Reserved for the result(s)
            #   returned by the simple detection algorithm(s)
            _,

            #"" (0, 0, 0, 0, 0, 0, 0, 0,) - Reserved (ex: readGPS)
            _,

            _, # gpsInfo["position"]["latitude"],
            _, # gpsInfo["position"]["longitude"],
            _, # gpsInfo["position"]["altitude"],
            _, # gpsInfo["position"]["vertical_accuracy"],
            _, # gpsInfo["position"]["horizontal_accuracy"],
            _, # gpsInfo["course"]["speed"],
            _, # gpsInfo["course"]["heading"],
            _, # gpsInfo["course"]["heading_accuracy"],
            _, # gpsInfo["course"]["speed_accuracy"],
            _, # gpsInfo["satellites"]["horizontal_dop"],
            _, # gpsInfo["satellites"]["vertical_dop"],
            _, # gpsInfo["satellites"]["time_dop"],
            _, # gpsInfo["satellites"]["time"],
            _, # gpsInfo["satellites"]["used_satellites"],
            _, # gpsInfo["satellites"]["satellites"],
            _, # 0, 0, 0, 0, 0, 0, 0, 0, - reserved
            _, # 0, 0, 0, 0, 0, 0, 0, 0, - reserved

            # logAccelerometerAndRotationSensors, "" (0, 0, 0, 0, 0, 0, 0,)
            #   reserved (ex: light sensor, temperature, humidity,
            #   body physiology, tap sensor, gyro, etc)
            _, _,

            _, # 0, 0, 0, 0, 0, 0, 0, 0, reserved
            _, # 0, 0, 0, 0, 0, 0, 0, 0, reserved for values of these sensors
            _, # 0, 0, 0, 0, 0, 0, 0, 0, reserved for values of these sensors
            _, # 0, 0, 0, 0, 0, 0, 0, 0, reserved for values of these sensors
            _, # 0, 0, 0, 0, 0, 0, 0, 0, reserved for values of these sensors
            _, # 0, 0, 0, 0, 0, 0, 0, 0, reserved for values of these sensors
            _, # 0, 0, 0, 0, 0, 0, 0, 0, reserved for values of these sensors
            _, # 0, 0, 0, 0, 0, 0, 0, 0, reserved for values of these sensors
            _, # 0, 0, 0, 0, 0, 0, 0, 0, reserved for values of these sensors
            _, # 0, 0, 0, 0, 0, 0, 0, 0, reserved for values of these sensors
            _, # 0, 0, 0, 0, 0, 0, 0, 0, reserved for values of these sensors
            _, # 0, 0, 0, 0, 0, 0, 0, 0, reserved for values of these sensors
            _, # 0, 0, 0, 0, 0, 0, 0, 0, reserved for values of these sensors

            _, dawnTimeVec[0], dawnTimeVec[1], dawnTimeVec[2], _,
            duskTimeVec[0], duskTimeVec[1], duskTimeVec[2], _, #"" (20s), etc

            _, _, ICAM_SERVER_NAME, # fileNameStr, ICAM_SERVER_NAME
            crc32
        ) = struct.unpack(statePackFormat,
            # Length of string given as arg in unpack() must be calcsize(fmt).
                            fileData[0 : stateSize])

        firstNullCharIndex = accessPointName.find("\x00")
        if firstNullCharIndex != -1:
            accessPointName = accessPointName[0 : firstNullCharIndex]
        # print "len(fileName) =", len(fileName)
        accessPointName = unicode(accessPointName)

        firstNullCharIndex = bluetoothServerAddress.find("\x00")
        if firstNullCharIndex != -1:
            bluetoothServerAddress = \
                        bluetoothServerAddress[0 : firstNullCharIndex]
        # print "len(fileName) =", len(fileName)

        firstNullCharIndex = ICAM_SERVER_NAME.find("\x00")
        if firstNullCharIndex != -1:
            ICAM_SERVER_NAME = ICAM_SERVER_NAME[0 : firstNullCharIndex]


        global bluetoothMode
        if bluetoothServerAddress == "":
            bluetoothMode = -1
        elif bluetoothServerAddress == "no_BT":
            bluetoothMode = 0
            bluetoothServerAddress = ""
        elif bluetoothServerAddress == "BTServer":
            bluetoothMode = 1
            bluetoothServerAddress = ""
        else:
            bluetoothMode = 2
    except:
        if myDebugStderr:
            traceback.print_exc()
            sys.stderr.flush()
        return False

    try:
        if myDebugStdout:
            print "LoadStateFromFile(): accessPointName = %s, " \
                    "bluetoothServerAddress = %s, bluetoothMode = %d, " \
                    "pauseInterval = %d, " \
                    "cameraMode[0] = %d, cameraMode[1] = %d, " \
                    "burstModeIsStarted = %d, photoResolutionIndex = %d, " \
                    "localPhotoResolutionIndex[0] = %d, " \
                    "localPhotoResolutionIndex[1] = %d" % \
                    (
                        accessPointName,
                        bluetoothServerAddress, bluetoothMode,
                        pauseInterval,
                        cameraMode[0], cameraMode[1],
                        burstModeIsStarted, photoResolutionIndex,
                        localPhotoResolutionIndex[0],
                        localPhotoResolutionIndex[1],
                    )

            print "LoadStateFromFile(): " \
                    "photoModeIndex[0] = %d, photoModeIndex[1] = %d, " \
                    "digitalZoom = %d, photoQuality = %d, " \
                    "exposureIndex[0] = %d, exposureIndex[1] = %d, " \
                    "BATTERY_LEVEL_THRESHOLD = %d." % \
                    (
                        photoModeIndex[0], photoModeIndex[1],
                        digitalZoom, photoQuality,
                        exposureIndex[0], exposureIndex[1],
                        BATTERY_LEVEL_THRESHOLD,
                    )

            print "LoadStateFromFile(): " \
                    "whiteBalanceIndex[0] = %d, whiteBalanceIndex[1] = %d, " \
                    "flashIndex = %d." % \
                    (
                        whiteBalanceIndex[0], whiteBalanceIndex[1],
                        flashIndex
                    )

            print "LoadStateFromFile(): audioRecordDuration = %d, " \
                    "videoRecordDuration[0] = %d, videoRecordDuration[1] = %d, " \
                    "videoAudioEnabled = %d, uploadUnsentData = %d, " \
                    "storeLocallyMedia = %d, motionDetectionIsOn = %d." % \
                    (
                        audioRecordDuration,
                        videoRecordDuration[0], videoRecordDuration[1],
                        videoAudioEnabled, uploadUnsentData,
                        storeLocallyMedia, motionDetectionIsOn,
                    )
            print "LoadStateFromFile(): MY_DEBUG_STDOUT = %d, " \
                    "MY_DEBUG_STDERR = %d, MY_DEBUG_STDERR_2 = %d, " \
                    "MY_DEBUG_UPLOAD_MSG = %d." % \
                        (MY_DEBUG_STDOUT, MY_DEBUG_STDERR,
                            MY_DEBUG_STDERR_2, MY_DEBUG_UPLOAD_MSG)

            print "LoadStateFromFile(): startAutomatically = %d.\n" % \
                    startAutomatically
            sys.stdout.flush()
    except:
        if myDebugStderr:
            traceback.print_exc()
            sys.stderr.flush()
        return True

    return True




NO_MEDIA_FILE_NAME = "[!NO_FILE]"
"""
IMPORTANT: The (local) state does NOT contain the deviceId.
"""
def BuildState(cameraId, crtTime, numMilliseconds, fileName, pathFileName):
    global accessPointName, bluetoothMode, bluetoothServerAddress
    global signalUnits, signalStrength, pauseInterval, \
        burstModeIsStarted, photoResolutionIndex, \
        localPhotoResolutionIndex
    global photoModeIndex, digitalZoom, photoQuality, exposureIndex, \
        whiteBalanceIndex, flashIndex
    global audioRecordDuration, videoRecordDuration
    global localVideoModeIndex, cameraMode, videoAudioEnabled
    global rotateDegreesImage, mobileCountryCode, mobileNetworkCode, \
        locationAreaCode, cellId, gpsInfo
    global statePackFormat, storeLocallyMedia
    global differentPixelsPercentageThreshold
    global modeManagerIsEnabled, dawnTimeVec, duskTimeVec
    global uploadHowManyOfLatestBluetoothMessages, uploadUnsentData, \
        saveUnsentPackets
    global uploadMediaToYouTube, uploadMediaToPicasa, useiCamServer
    global internetUploadMaxErrors
    global startAutomatically
    global BATTERY_LEVEL_THRESHOLD
    global reactiveLoopOpsIndex
    #global sentBTMessageTo6680
    global MY_DEBUG_STDOUT, MY_DEBUG_STDERR, MY_DEBUG_STDERR_2, \
        MY_DEBUG_UPLOAD_MSG
    global startViewfinderBeforeTakingPhoto

    DebugPrint("Entered BuildState().")

    try:
        """
        if MY_DEBUG_UPLOAD_MSG:
            UploadText("Sending photo saved in %s." % photoPathFileName,
                ICAM_SERVER_NAME, WEBPAGE_UL_GZIPPED_TEXT)
        """

        """
        myText = "Battery = %d. Photo interval (pauseInterval) = %d. " \
                    "burstModeIsStarted = %d. " \
                    "Resolution (photoResolutionIndex) = %d. " \
                    "digitalZoom = %d. photoQuality = %d." \
                    % (GetBatteryLevelPercentage(), pauseInterval,
                        burstModeIsStarted,
                        photoResolutionIndex,
                        digitalZoom, photoQuality)
        """

        """
        UploadText("Battery = %d. Photo interval (pauseInterval) = %d. " \
                    "burstModeIsStarted = %d. Resolution = %d x %d. " \
                    "digitalZoom = %d. photoQuality = %d." \
                        % (GetBatteryLevelPercentage(), pauseInterval,
                            burstModeIsStarted,
                            photoResolutionStr[photoResolutionIndex][1][0],
                            photoResolutionStr[photoResolutionIndex][1][1],
                            digitalZoom, photoQuality),
                    ICAM_SERVER_NAME, WEBPAGE_UL_GZIPPED_TEXT)
        """

        """
        if MY_DEBUG_UPLOAD_MSG:
            UploadGZippedData(photoFileName, ...., ICAM_SERVER_NAME,
                WEBPAGE_UL_GZIPPED_STATE_AND_FILE)
        """
        ReadGPSPosition()

        mediaFileSize = -1
        if (fileName is None) or (fileName == NO_MEDIA_FILE_NAME):
        #if fileName is None:
            fileNameStr = ""
        else:
            fileNameStr = fileName
            try:
                mediaFileSize = os.path.getsize(pathFileName)
            except:
                DebugPrintErrorTrace()

        if bluetoothMode == 0:
            tmpBluetoothServerAddress = "no_BT"
        elif bluetoothMode == 1:
            tmpBluetoothServerAddress = "BTServer"
        else:
            tmpBluetoothServerAddress = bluetoothServerAddress

        """
        print "cameraId = %d" % cameraId
        print "aYear = %d" % aYear
        print "aMonth = %d" % aMonth
        print "aDay = %d" % aDay
        print "aHour = %d" % aHour
        print "aMinute = %d" % aMinute
        print "aSecond = %d" % aSecond
        print "GetBatteryLevelPercentage() = %d" % GetBatteryLevelPercentage()
        print 'GetFreeDriveSpace("E:") = %d' % GetFreeDriveSpace("E:")
        print "GetFreeRAM() = %d" % GetFreeRAM()
        #signalUnits
        print "signalStrength = %d" %signalStrength
        print "pauseInterval = %d" % pauseInterval
        print "burstModeIsStarted = %d" % burstModeIsStarted
        print "photoResolutionIndex = %d" % photoResolutionIndex
        print "localPhotoResolutionIndex[0] = %d" % localPhotoResolutionIndex[0]
        print "localPhotoResolutionIndex[1] = %d" % localPhotoResolutionIndex[1]
        print "photoModeIndex[0] = %d" % photoModeIndex[0]
        print "photoModeIndex[1] = %d" % photoModeIndex[1]
        print "digitalZoom = %d" % digitalZoom
        print "photoQuality = %d" % photoQuality
        print "exposureIndex[0] = %d" % exposureIndex[0]
        print "exposureIndex[1] = %d" % exposureIndex[1]
        print "whiteBalanceIndex[0] = %d" % whiteBalanceIndex[0]
        print "whiteBalanceIndex[1] = %d" % whiteBalanceIndex[1]
        print "flashIndex = %d" % flashIndex
        print "audioRecordDuration = %d" % audioRecordDuration
        print "videoRecordDuration[0] = %d" % videoRecordDuration[0]
        print "videoRecordDuration[1] = %d" % videoRecordDuration[1]
        print "rotateDegreesImage = %d" % rotateDegreesImage
        print "mobileCountryCode = %d" % mobileCountryCode
        print "mobileNetworkCode = %d" % mobileNetworkCode
        print "locationAreaCode = %d" % locationAreaCode
        print "cellId = %d" % cellId

        print "gpsInfo["position"]["latitude"] = %f" % \
            gpsInfo["position"]["latitude"]

        print "gpsInfo["position"]["longitude"] = %f" % \
            gpsInfo["position"]["longitude"]

        print "gpsInfo["position"]["altitude"] = %f" % \
            gpsInfo["position"]["altitude"]

        print "gpsInfo["position"]["vertical_accuracy"] = %f" % \
            gpsInfo["position"]["vertical_accuracy"]

        print "gpsInfo["position"]["horizontal_accuracy"] = %f" % \
            gpsInfo["position"]["horizontal_accuracy"]

        print "gpsInfo["course"]["speed"] = %f" % \
            gpsInfo["course"]["speed"]

        print "gpsInfo["course"]["heading"] = %f" % \
            gpsInfo["course"]["heading"]

        print "gpsInfo["course"]["heading_accuracy"] = %f" % \
            gpsInfo["course"]["heading_accuracy"]

        print "gpsInfo["course"]["speed_accuracy"] = %f" % \
            gpsInfo["course"]["speed_accuracy"]
        print "gpsInfo["satellites"]["horizontal_dop"] = %f" % \
            gpsInfo["satellites"]["horizontal_dop"]

        print "gpsInfo["satellites"]["vertical_dop"] = %f" % \
            gpsInfo["satellites"]["vertical_dop"]

        print "gpsInfo["satellites"]["time_dop"] = %f" % \
            gpsInfo["satellites"]["time_dop"]

        print "gpsInfo["satellites"]["time"] = %f" % \
            gpsInfo["satellites"]["time"]

        print "gpsInfo["satellites"]["used_satellites"] = %d" % \
            gpsInfo["satellites"]["used_satellites"]

        print "gpsInfo["satellites"]["satellites"] = %d" % \
            gpsInfo["satellites"]["satellites"]
        """

        #DebugPrint("BuildState(): marshalling state.")

        stateMarshalled = struct.pack(statePackFormat00,
                                    struct.calcsize(statePackFormat))

        #DebugPrint("BuildState(): marshalling state0.")

        # IMPORTANT NOTE: b = signed char.
        # statePackFormat01 = "<bbbbiiiiibbbbbbbb"
        # !!!!I should take out uploadMediaToYouTube, uploadMediaToPicasa,
        #    useiCamServer from here since I store them in
        #    LOCAL_CONFIG_PATH_FILENAME and would create some confusion if putting
        #    them here - there is one reason to put them here: we can inform
        #    the server what the state is.
        stateMarshalled += struct.pack(statePackFormat01,
            # IMPORTANT:
            # uploadMediaToYouTube, uploadMediaToPicasa, useiCamServer are being
            #    stored in both the state and config data.
            #    However, they are reloaded only from the config file iCam.cfg.
            uploadMediaToYouTube, uploadMediaToPicasa, useiCamServer,
            internetUploadMaxErrors, BATTERY_LEVEL_THRESHOLD, # !!!!Maybe put it next to the GetBatteryLevelPercentage() field
            reactiveLoopOpsIndex, sentBTMessageTo6680, 0, 0, #0, #!!!!Put it closer to startAutomatically
            mediaFileSize, 0,
            uploadHowManyOfLatestBluetoothMessages, modeManagerIsEnabled,
            startAutomatically, saveUnsentPackets, uploadUnsentData,
            MY_DEBUG_STDOUT, MY_DEBUG_STDERR, MY_DEBUG_STDERR_2,
            MY_DEBUG_UPLOAD_MSG)
        #DebugPrint("BuildState(): marshalling state1.")

        btL = [int(e, 16) for e in bluetoothSelfAddress.split(":")]

        stateMarshalled += struct.pack(statePackFormat02,
                                str(accessPointName),
                                btL[0], btL[1], btL[2], btL[3], btL[4], btL[5],
                                str(tmpBluetoothServerAddress),
                                cameraId)
        #DebugPrint("BuildState(): marshalling state2.")

        stateMarshalled += struct.pack(statePackFormat03,
                            crtTime.tm_year, crtTime.tm_mon, crtTime.tm_mday,
                            crtTime.tm_hour, crtTime.tm_min, crtTime.tm_sec,
                            numMilliseconds)

        stateMarshalled += struct.pack(statePackFormat03b, int(GetTime()), 0, 0)

        #DebugPrint("BuildState(): marshalling state3.")

        #stateMarshalled += struct.pack(statePackFormat04,
        #        GetBatteryLevelPercentage(), GetFreeDriveSpace("C:"),
        #        0, GetFreeDriveSpace("E:"), 0, GetFreeRAM(), 0)

        stateMarshalled += struct.pack(statePackFormat04,
                            GetBatteryLevelPercentage(),
                            GetFreeDriveSpace("C:"), GetFreeDriveSpace("E:"),
                            GetFreeRAM(), 0, GetChargerStatus())
        #stateMarshalled += struct.pack(statePackFormat04, 0, 0, 0, 0, 0, 0, 0)

        #DebugPrint("BuildState(): marshalling state4.")
        stateMarshalled += struct.pack(statePackFormat05, signalUnits[:2],
                                        signalStrength)

        #DebugPrint("BuildState(): marshalling state5.")

        stateMarshalled += struct.pack(statePackFormat06,
            pauseIntervalGdata,
            pauseInterval, burstModeIsStarted, photoResolutionIndex, 0,
            localPhotoResolutionIndex[0], localPhotoResolutionIndex[1],
            photoModeIndex[0])

        #DebugPrint("BuildState(): marshalling state6.")

        # First 0 is for opticalZoom
        stateMarshalled += struct.pack(statePackFormat07,
            photoModeIndex[1], digitalZoom,
            0, photoQuality, 0, exposureIndex[0], exposureIndex[1])

        #DebugPrint("BuildState(): marshalling state7.")

        stateMarshalled += struct.pack(statePackFormat08,
            whiteBalanceIndex[0], whiteBalanceIndex[1],
            flashIndex, startViewfinderBeforeTakingPhoto)

        #DebugPrint("BuildState(): marshalling state8.")

        stateMarshalled += struct.pack(statePackFormat09,
            """
            0, 0, 0, 0, 0, 0, 0, 0, - indices for:
                ExposureCompensation, ISO, contrast, sharpness, color tone,
                Scene Modes
            """
            "",
            """
            0, 0, 0, 0, 0, 0, 0, 0, - reserved (ex: ShutterSpeed)
            """
            "",
            """
            0, 0, 0, 0, 0, 0, 0, 0, - indices for
                localVideoMode, localVideoFrameRates, localVideoEncoding,
                Video Stabilization, Audio Recording, etc
                (Note: Camera app on S60 on N82 has presets: TV High Quality,
                TV Normal Quality, Email High Quality,
                Email Normal Quality, Sharing Quality)
            """
            "",
            """
            0, 0, 0, 0, 0, 0, 0, 0, #reserved (ex: sent videoResolution)
            """
            ""
            )

        #DebugPrint("BuildState(): marshalling state9.")

        stateMarshalled += struct.pack(statePackFormat10,
            videoRecordDuration[0], videoRecordDuration[1],
            int(localVideoModeIndex[0]), int(localVideoModeIndex[1]),
            cameraMode[0], cameraMode[1], videoAudioEnabled, 0)
        """
        DebugPrint("BuildState(): marshalling state10.")
        # 0, 0, 0, 0, 0, 0, 0, 0, - reserved (ex: audioType - MP3 or AMR)
        """

        stateMarshalled += struct.pack(statePackFormat11, "")

        """
        DebugPrint("BuildState(): marshalling state11.")

        # 0, 0, 0, 0, 0, 0, - reserved for other durations
        """

        stateMarshalled += struct.pack(statePackFormat12,
                                        audioRecordDuration, "")
        """
        DebugPrint("BuildState(): marshalling state12.")
        # 0, 0, 0, 0, 0, 0, 0,
        """

        stateMarshalled += struct.pack(statePackFormat13,
                                        rotateDegreesImage, "")
        #DebugPrint("BuildState(): marshalling state13.")

        stateMarshalled += struct.pack(statePackFormat14,
                mobileCountryCode, mobileNetworkCode, locationAreaCode,
                cellId)

        """
        DebugPrint("BuildState(): marshalling state14.")
        # 0, 0, 0, 0, 0, 0, 0, 0, - reserved
        """

        stateMarshalled += struct.pack(statePackFormat15, "")
        """
        DebugPrint("BuildState(): marshalling state15.")
        # 0, 0, 0, 0, 0, 0, 0, - reserved for "Choose when to send Unsent",
        #    plugged and charging or not
        """

        stateMarshalled += struct.pack(statePackFormat16,
                                        storeLocallyMedia, "")
        """
        DebugPrint("BuildState(): marshalling state16.")
        # 0, 0, 0, 0, 0, 0, 0, - reserved for noiseDetectionEnabled,
        #   faceDetection, soundDetection (a human talking, a dog barking,
        #   a bird singing)
        """

        stateMarshalled += struct.pack(statePackFormat17, motionDetectionIsOn,
                                    differentPixelsPercentageThreshold[0], "")
        #DebugPrint("BuildState(): marshalling state17.")

        stateMarshalled += struct.pack(statePackFormat18,
                """
                0, 0, 0, 0, 0, 0, 0, 0, - reserved for motion detection
                    params (thresholds, etc)
                """
                "",
                """
                0, 0, 0, 0, 0, 0, 0, 0, - reserved for the result(s) returned
                    by the simple detection algorithm(s)
                """
                "",
                """
                0, 0, 0, 0, 0, 0, 0, 0, - reserved for the result(s) returned
                    by the simple detection algorithm(s)
                """
                "",
                """
                0, 0, 0, 0, 0, 0, 0, 0, - reserved for the result(s) returned
                    by the simple detection algorithm(s)
                """
                ""
                )
        """
        DebugPrint("BuildState(): marshalling state18.")
        # 0, 0, 0, 0, 0, 0, 0, 0, - reserved (ex: readGPS)
        """

        stateMarshalled += struct.pack(statePackFormat19, "")
        #DebugPrint("BuildState(): marshalling state19.")

        stateMarshalled += struct.pack(statePackFormat20,
            gpsInfo["position"]["latitude"],
            gpsInfo["position"]["longitude"],
            gpsInfo["position"]["altitude"],
            gpsInfo["position"]["vertical_accuracy"],
            gpsInfo["position"]["horizontal_accuracy"],
            gpsInfo["course"]["speed"],
            gpsInfo["course"]["heading"],
            gpsInfo["course"]["heading_accuracy"],
            gpsInfo["course"]["speed_accuracy"],
            gpsInfo["satellites"]["horizontal_dop"],
            gpsInfo["satellites"]["vertical_dop"],
            gpsInfo["satellites"]["time_dop"],
            gpsInfo["satellites"]["time"],
            gpsInfo["satellites"]["used_satellites"],
            gpsInfo["satellites"]["satellites"]
        )
        #DebugPrint("BuildState(): marshalling state20.")

        # 0, 0, 0, 0, 0, 0, 0, 0, #reserved
        # 0, 0, 0, 0, 0, 0, 0, 0, #reserved
        stateMarshalled += struct.pack(statePackFormat21, "", "")
        """
        DebugPrint("BuildState(): marshalling state21.")
        # 0, 0, 0, 0, 0, 0, 0, - reserved (ex: light sensor, temperature,
        #    humidity, body physiology, tap sensor, gyro, etc)
        """

        stateMarshalled += struct.pack(statePackFormat22,
                logAccelerometerAndRotationSensors, "")
        #DebugPrint("BuildState(): marshalling state22.")

        stateMarshalled += struct.pack(statePackFormat23,
            "", # 0, 0, 0, 0, 0, 0, 0, 0 - reserved for values of these sensors
            "", # 0, 0, 0, 0, 0, 0, 0, 0 - reserved for values of these sensors
            "", # 0, 0, 0, 0, 0, 0, 0, 0 - reserved for values of these sensors
            "", # 0, 0, 0, 0, 0, 0, 0, 0 - reserved for values of these sensors
            "", # 0, 0, 0, 0, 0, 0, 0, 0 - reserved for values of these sensors
            "", # 0, 0, 0, 0, 0, 0, 0, 0 - reserved for values of these sensors
            "", # 0, 0, 0, 0, 0, 0, 0, 0 - reserved for values of these sensors
            "", # 0, 0, 0, 0, 0, 0, 0, 0 - reserved for values of these sensors
            "", # 0, 0, 0, 0, 0, 0, 0, 0 - reserved for values of these sensors
            "", # 0, 0, 0, 0, 0, 0, 0, 0 - reserved for values of these sensors
            "", # 0, 0, 0, 0, 0, 0, 0, 0 - reserved for values of these sensors
            "", # 0, 0, 0, 0, 0, 0, 0, 0 - reserved for values of these sensors
            ""  # 0, 0, 0, 0, 0, 0, 0, 0 - reserved for values of these sensors
            )

        stateMarshalled += struct.pack(statePackFormat24,
            "", # 0, 0, 0, 0, 0, 0, 0, 0 - reserved for values of these sensors
            dawnTimeVec[0], dawnTimeVec[1], dawnTimeVec[2], 0,
            duskTimeVec[0], duskTimeVec[1], duskTimeVec[2], 0)

        stateMarshalled += struct.pack(statePackFormat25, 0)

        stateMarshalled += struct.pack(statePackFormat26, fileNameStr)

        stateMarshalled += struct.pack(statePackFormat27, ICAM_SERVER_NAME)

        crc32 = binascii.crc32(stateMarshalled)
        stateMarshalled += struct.pack(statePackFormat28, crc32)

        return stateMarshalled

        #DebugPrint("BuildState(): marshalled the state.")
    except:
        DebugPrintErrorTrace()

###############################################################################
###############################################################################
########################END STATE LOAD/STORE FUNCTIONS#########################
###############################################################################
###############################################################################



############################SETTINGS FOR MY PHONES#############################

# N95 - remarkable moment to have a deffect (wo working display) N95
#    as Inet proxy ;)
# INTERNET_PROXY_PHONE_DEVICE_ID = IMEI_N95

# N82
# This is the master in a BT network
INTERNET_PROXY_PHONE_DEVICE_ID = IMEI_N82
INTERNET_PROXY_PHONE_BLUETOOTH_ADDRESS = BT_ADDR_N82

# PERSONAL SPECIFIC
if deviceId == IMEI_E7:
    # MY_DEBUG_UPLOAD_MSG = False
    MY_DEBUG_UPLOAD_MSG = False
    #USE_ICAM_SERVER = False
    #USE_ICAM_SERVER = True
    #ICAM_SERVER_NAME = ICAM_GAE_SERVER_NAME

    BATTERY_LEVEL_THRESHOLD = 40

    """
    MY_DEBUG_STDOUT = False
    MY_DEBUG_STDERR = False
    MY_DEBUG_STDERR_2 = False
    MY_DEBUG_UPLOAD_MSG = False  # For uploaded log messages
    """

elif deviceId == IMEI_6120:
    LOCAL_FOLDER = "C:/iCam"
    if DoesDriveExist("E:"):
        LOCAL_FOLDER = "E:/iCam"

    uploadMediaToYouTube = 0
    uploadMediaToPicasa = 0
    useiCamServer = 2
    googleUsername = "googleUser"

    BATTERY_LEVEL_THRESHOLD = 60

    startButtonPressed = True

    saveUnsentPackets = 0 #True

    LOCAL_FOLDER_MEDIA_FILES = LOCAL_FOLDER + "/Media"
    LOCAL_FOLDER_UNSENT_FILES = LOCAL_FOLDER + "/Unsent"
    """
    LOCAL_FOLDER_AUX = "D:/iCam"
    if not os.path.exists(LOCAL_FOLDER_AUX):
        os.makedirs(LOCAL_FOLDER_AUX)
    """
    if False:
        LOCAL_FOLDER_MEDIA_FILES = LOCAL_FOLDER_TEMP + "/Media"
        ERASE_ORIGINAL_MEDIA_FILE_AFTER_READ = True
    LOCAL_FOLDER_MEDIA_FILES = LOCAL_FOLDER + "/Media"
    ERASE_ORIGINAL_MEDIA_FILE_AFTER_READ = False

    # accessPointName = u"RDSPP"
    bluetoothMode = 2  # BT client
    bluetoothServerAddress = INTERNET_PROXY_PHONE_BLUETOOTH_ADDRESS

    STATE_PATH_FILENAME = "C:/iCam/" + STATE_FILENAME
    LOCAL_CONFIG_PATH_FILENAME = "C:/iCam/" + LOCAL_CONFIG_FILENAME

    MY_DEBUG_STDOUT = False
    MY_DEBUG_STDERR = False
    MY_DEBUG_STDERR_2 = False
    MY_DEBUG_UPLOAD_MSG = False  # For uploaded log messages

elif deviceId == IMEI_N95:
    LOCAL_FOLDER = "C:/iCam"
    LOCAL_CONFIG_PATH_FILENAME = LOCAL_FOLDER + "/" + LOCAL_CONFIG_FILENAME
    LOCAL_FOLDER_SENT_LOGS = LOCAL_FOLDER + "/LogsSent"
    # LOCAL_FOLDER = "E:/iCam"
    LOCAL_FOLDER_UNSENT_FILES = LOCAL_FOLDER + "/Unsent"
    startButtonPressed = True

    #saveUnsentPackets = True
    saveUnsentPackets = 0 #True

    """
    This instructs phone not to resize photos taken and send them
        at original resolution directly.
    """
    MODE_FOR_PHONE_WITH_LITTLE_RAM_AND_UNRELIABLE_MEM_CARD = True

    # To avoid resize also choose Sent res to (1280, 980)
    # LOCAL_FOLDER_AUX = "C:/iCam"
    """
    LOCAL_FOLDER_AUX = "D:/iCam"
    if not os.path.exists(LOCAL_FOLDER_AUX):
        os.makedirs(LOCAL_FOLDER_AUX)
    """
    LOCAL_FOLDER_MEDIA_FILES = LOCAL_FOLDER_TEMP + "/Media"
    ERASE_ORIGINAL_MEDIA_FILE_AFTER_READ = True
    cameraMode[0] = 1
    cameraMode[1] = 1
    videoRecordDuration[0] = 30
    videoRecordDuration[1] = 30

    """
    # MY_DEBUG_STDOUT = False
    MY_DEBUG_STDOUT = True
    MY_DEBUG_STDERR = True
    MY_DEBUG_STDERR_2 = True
    MY_DEBUG_UPLOAD_MSG = True  # For uploaded log messages
    """
    MY_DEBUG_STDOUT = False
    MY_DEBUG_STDERR = False
    MY_DEBUG_STDERR_2 = False
    MY_DEBUG_UPLOAD_MSG = False  # For uploaded log messages

    bluetoothMode = 0  # no BT
    # bluetoothMode = 1 #BT server
    uploadUnsentData = 2  # Send logs

elif deviceId == IMEI_6680:
    LOCAL_FOLDER = "C:/iCam"
    if DoesDriveExist("E:"):
        LOCAL_FOLDER = "E:/iCam"

    uploadMediaToYouTube = 0
    uploadMediaToPicasa = 0
    useiCamServer = 2
    googleUsername = "googleUser"

    BATTERY_LEVEL_THRESHOLD = 99

    startButtonPressed = True
    """
    This instructs phone not to resize photos taken and send them
        at original resolution directly.
    """
    MODE_FOR_PHONE_WITH_LITTLE_RAM_AND_UNRELIABLE_MEM_CARD = True
    # To avoid resize also choose Sent res to (1280, 980)
    # LOCAL_FOLDER_AUX = "C:/iCam"
    """
    LOCAL_FOLDER_AUX = "D:/iCam"
    if not os.path.exists(LOCAL_FOLDER_AUX):
        os.makedirs(LOCAL_FOLDER_AUX)
    """
    LOCAL_FOLDER_MEDIA_FILES = LOCAL_FOLDER_TEMP + "/Media"
    # LOCAL_FOLDER_UNSENT_FILES = LOCAL_FOLDER_AUX + "/Unsent"
    # LOCAL_FOLDER_UNSENT_FILES = LOCAL_FOLDER + "/Unsent"
    LOCAL_FOLDER_UNSENT_FILES = LOCAL_FOLDER + "/Unsent"
    # LOCAL_FOLDER_UNSENT_FILES = "D:/Unsent"
    ERASE_ORIGINAL_MEDIA_FILE_AFTER_READ = True

    bluetoothMode = 2  # BT client
    bluetoothServerAddress = INTERNET_PROXY_PHONE_BLUETOOTH_ADDRESS

    """
    Saving the state in C:\iCam - avoiding writing much on the mem-card,
        since 6680 sometimes corrupts mem-cards...
    """
    STATE_PATH_FILENAME = "C:/iCam/" + STATE_FILENAME
    LOCAL_CONFIG_PATH_FILENAME = "C:/iCam/" + LOCAL_CONFIG_FILENAME

    photoResolutionIndex = 10  # To allow sending the photo at Max res
    cameraMode[0] = 1
    cameraMode[1] = 1
    # videoRecordDuration[0] = 25
    # videoRecordDuration[1] = 15
    videoRecordDuration[0] = 30
    videoRecordDuration[1] = 30
    # Don't send any unsent data (maybe make it 1!!!!)
    uploadUnsentData = 0

    """
    MY_DEBUG_STDOUT = False
    MY_DEBUG_STDERR = False
    MY_DEBUG_STDERR_2 = False
    MY_DEBUG_UPLOAD_MSG = False  # For uploaded log messages
    """

elif deviceId == IMEI_N82:
    LOCAL_FOLDER = "C:/iCam"
    if DoesDriveExist("E:"):
        LOCAL_FOLDER = "E:/iCam"

    uploadMediaToYouTube = 0
    uploadMediaToPicasa = 0
    useiCamServer = 2
    googleUsername = "googleUser"

    BATTERY_LEVEL_THRESHOLD = 50

    bluetoothMode = 1  # BT server

    uploadHowManyOfLatestBluetoothMessages = 4

    # startButtonPressed = True
    """
    LOCAL_FOLDER_AUX = "D:/iCam"
    if not os.path.exists(LOCAL_FOLDER_AUX):
        os.makedirs(LOCAL_FOLDER_AUX)
    LOCAL_FOLDER_MEDIA_FILES = LOCAL_FOLDER_AUX + "/Media"
    """
    LOCAL_FOLDER_MEDIA_FILES = LOCAL_FOLDER + "/Media"
    LOCAL_FOLDER_UNSENT_FILES = LOCAL_FOLDER + "/Unsent"
    ERASE_ORIGINAL_MEDIA_FILE_AFTER_READ = True
    STATE_PATH_FILENAME = "C:/iCam/" + STATE_FILENAME
    LOCAL_CONFIG_PATH_FILENAME = "C:/iCam/" + LOCAL_CONFIG_FILENAME
elif RASPBIAN_OS == True: #!!!!TODO: check deviceId
    bluetoothMode = 1  # BT server
    #accessPointRetryConnect = True #False
    #MY_DEBUG_STDOUT = False
    #MY_DEBUG_STDERR = False
    #MY_DEBUG_STDERR_2 = False
    MY_DEBUG_UPLOAD_MSG = False  # For uploaded log messages


STATE_PATH_FILENAME_BACKUP = STATE_PATH_FILENAME + STATE_BACKUP_EXTENSION
# END PERSONAL SPECIFIC

# CreateDirectoriesAndLogFiles()

###############################################################################
###############################################################################
###############################################################################
#################################IMPORTS#######################################
###############################################################################
###############################################################################
###############################################################################

import thread
import zlib


def MyThreadStart(myFunc, funcParams=()):
    #DebugPrint("Entered MyThreadStart().")

    if SYMBIAN_OS:
        if not _PyS60_1_9_OR_NEWER: #pyS60VersionNumber <= 14
            """
            thread.start_new_thread does not work well on PyS60 1.4.5 - see,
                for example,
                http://www.developer.nokia.com/Community/Discussion/showthread.php?172950-Starting-a-new-thread-silently-fails-in-N97-%28PyS60-1.4.5%29
            """
            myFunc()

            #DebugPrint("Exiting MyThreadStart().")
            return

    # !!!!Use a thread pool - maybe use the one from Twisted
    thread.start_new_thread(myFunc, funcParams)

    #DebugPrint("Exiting MyThreadStart().")


"""
I put this code here because I want to create ASAP the log files and see in
    the log files any error generated by importing spcecial modules, etc.
Unfortunately, there is no other way - see for ex.
    http://stackoverflow.com/questions/1590608/is-it-possible-to-forward-declare-a-function-in-python.
# !!!!For nicer code I should put GetCurrentDateTime() and
    CreateDirectoriesAndLogFiles() in a separate module and import it here.

Note: Before creating the log files, the stdout/err will output to the screen.
    I tried to avoid writing on the screen, which is not nice maybe for the user:
        - see LoadStateFromFile() - it has disabled the writing to stdout/err
            before executing CreateDirectoriesAndLogFiles();
        - TODO!!!!: MoveFileBetweenAnyDrives() (used by LoadStateFromFile())
            however does NOT disable the writing to stdout/err;
        - maybe? there are other functions/global pieces of code writing
            before to stdout/err.

"""


def GetTime():
    """
    Note that S60 2nd edition has GetTime() with resolution of 1
        second (not milliseconds, as in S60 3rd ed, and the rest).
    """

    if SYMBIAN_S60_OS:
        """
        PyS60 1.4.5 uses rather standard way to get time(): see timemodule.c.
         I guess this is why we require to decr time.timezone (on FP2 at least)

        PyS60 2.0 uses a different way to obtain the time - see PyS60 src code
          file \src\newcore\Symbian\src\symbian_adaptation.cpp, function
            double symbian_clock()
            {
                TInt period = 1000*1000;

                HAL::Get(HALData::ESystemTickPeriod, period);

                /* Return TickCount in seconds */
                return (double)User::TickCount() * ((double)period / (1000.0 * 1000.0));
            }
        """
        #if SYMBIAN_S60_2ND_ED:  # (S60_EDITION[0] < 3)):
        #if (phoneModel == "Nokia6680") or (phoneModel == "NokiaN70"):
        if sys.version_info[0 : 2] == (2, 2):
            """
            PyS60 1.4.5 (running on S60 2nd edition, but also on other phones)
                has the issue with displaying the current hour w.r.t. ~UTC.

            Tested this on PyS60 1.4.5 on E7 (normally 2nd edition phones).

            !!!!TODO: Test on (other) phones with time zone setting different
                than Bucharest, Romania
            #!!!!TEST on S60 1st ed

            From http://docs.python.org/2/library/time.html:
              time.timezone
                "The offset of the local (non-DST) timezone, in seconds west
              of UTC (negative in most of Western Europe, positive in the US,
              zero in the UK)."
            """
            #return time.time() - 2 * 3600
            return time.time() + time.timezone

    return time.time()


# The type of this one is a struct? localtime
def GetCurrentDateTime():
    # global phoneModel
    if SYMBIAN_OS:
        return time.localtime(GetTime())
    elif ANDROID_OS:
        return time.localtime()
    elif iOS_PYOBJC:
        return time.localtime()
    elif WINDOWS_OS:
        return time.localtime()
    elif UNIX_OS:
        return time.localtime()
    elif WINDOWS_CE_OS_PYTHONCE:
        return time.localtime()
    elif RASPBIAN_OS:
        return time.localtime()


def TimeSyncForTimeResettingPhones():
    """
    This is kind of useless time sync, useful only if we have a phone
        without Internet connectivity.
      This situation is helpful AT LEAST for my Nokia 6680, where the time is
        reset when taking out the battery.
    "Preserve" the flow of time locally:
        if we detect that the time is now lower than what it was last time
        iCam was run, in state.bin, we assume that the time in state.bin is
        correct and set it there.
    """

    try:
        # !!!Maybe do also if stateTime < GetTime() - 3 months, etc?
        if stateTime > GetTime():
            if SYMBIAN_OS:
                e32.set_home_time(stateTime)
    except:
        DebugPrintErrorTrace()



stdoutFile = None
stderrFile = None
stdoutFileName = None
stderrFileName = None


def CreateDirectoriesAndLogFiles():
    global stdoutFile, stderrFile
    global stdoutFileName, stderrFileName
    global LOCAL_FOLDER, LOCAL_FOLDER_MEDIA_FILES, LOCAL_FOLDER_UNSENT_FILES
    """
    global STATE_PATH_FILENAME
    STATE_PATH_FILENAME = LOCAL_FOLDER + "/" + STATE_FILENAME
    """

    try:
        """
        !!!!Check also that these folders are not files with isfile()...
        """
        if not os.path.exists(LOCAL_FOLDER):
            os.makedirs(LOCAL_FOLDER)
    except:
        if MY_DEBUG_STDERR:
            traceback.print_exc()
            #sys.stderr.flush()

    try:
        if not os.path.exists(LOCAL_FOLDER_MEDIA_FILES):
            os.makedirs(LOCAL_FOLDER_MEDIA_FILES)
    except:
        if MY_DEBUG_STDERR:
            traceback.print_exc()
            #sys.stderr.flush()

    try:
        # Note: On WinCE normally the Unsent media files are stored in .../DCIM
        if not os.path.exists(LOCAL_FOLDER_UNSENT_FILES):
            os.makedirs(LOCAL_FOLDER_UNSENT_FILES)
    except:
        if MY_DEBUG_STDERR:
            traceback.print_exc()
            #sys.stderr.flush()

    try:
        if not os.path.exists(LOCAL_FOLDER_TEMP):
            os.makedirs(LOCAL_FOLDER_TEMP)
    except:
        if MY_DEBUG_STDERR:
            traceback.print_exc()
            #sys.stderr.flush()

    try:
        STDDateTime = time.strftime("%Y_%m_%d_%H_%M_%S", GetCurrentDateTime())

        if MY_DEBUG_STDOUT:
            try:
                sys.stdout.flush()
            except:
                if MY_DEBUG_STDERR:
                    traceback.print_exc()
            """
            """

            """
            Nokia 6680 has little space (drive C - ~3MB) since it does not
                have an RS-MMC broke, so we can't store the long stdout
                log files.
            """
            # if deviceId != IMEI_6680:

            stdoutFileName = STDOUT_FILENAME_PREFIX + STDDateTime + ".txt"
            # if True:
            # if False:
            stdoutFile = open(LOCAL_FOLDER + "/" + stdoutFileName, "a")
            sys.stdout = stdoutFile
            sys.stdout.flush()

        if MY_DEBUG_STDERR:
            try:
                sys.stderr.flush()
            except:
                if MY_DEBUG_STDERR:
                    traceback.print_exc()

            stderrFileName = STDERR_FILENAME_PREFIX + STDDateTime + ".txt"
            # if True:
            # if False:
            stderrFile = open(LOCAL_FOLDER + "/" + stderrFileName, "a")
            sys.stderr = stderrFile
            sys.stderr.flush()
    except:
        DebugPrintErrorTrace()

"""
# BEFORE_MAIN:
We make here the call, because we want to have the STD files created ASAP:
    - to record errors ASAP.

We call LoadStateFromFile() before CreateDirectoriesAndLogFiles() because we
    want to initialize the MY_DEBUG_STDOUT/ERR variables which determine in
    CreateDirectoriesAndLogFiles() that we create the std*.txt files.
"""
LoadStateFromFile(STATE_PATH_FILENAME, myDebugStdout=False, myDebugStderr=False)

CreateDirectoriesAndLogFiles()

"""
(Minor): Chose to write error to log file, if any, instead of having log file with
    updated time.
"""
if deviceId == IMEI_6680:
    TimeSyncForTimeResettingPhones()

if deviceId == IMEI_E7:
    """
    For these to work we need to have the python files of the respective
        profiler installed on the phone.
    """
    try:
        import profile
        #import figleaf
        #figleaf.start()
        #import hotshot
    except:
        DebugPrintErrorTrace()


DebugPrint("sys.path = %s" % str(sys.path))


###############################################################################
###############################################################################
#################################TIME SYNC#####################################
###############################################################################
###############################################################################

ntplib = None
try:
    if sys.version_info[0 : 2] >= (2, 5):
        """
        ntplib uses decorators, not supported in Python 2.2.
          However, I guess we can easily make it work for Python 2.2,
              if ever required.
        """
        import ntplib
except:
    DebugPrint("Not able to import the ntplib module --> ntplib = %s." % \
                                                                str(ntplib))
    DebugPrintErrorTrace()


"""
We have 2 alternatives for the BT server to send the BT time sync:
    - BluetoothTimeSync() called in TimeSyncNTP(), which is called at the
            start of iCam. But this might be executed too often.
        BluetoothTimeSync() sends a BT message with exact time of the
                    BT server which should be already correct time.
    - BluetoothTimeSyncWithDrift(btClientDeviceId, btMsgSize)
        - sends an "adjust-date-and-time-using-delta" when notices that
            the message received in BluetoothMessageProcessAndDelete()
            has a very bit time drift.
The BT client for S60 sets in BluetoothMessageCallback(btMsgId) the
    arrival time of the BT time sync msg:
    dictBtMsgTime[btMsgId] = crtTime
   This time is used in BluetoothMessageProcessTIM(btMsgId).

"""

"""
We do NOT execute BluetoothTimeSyncWithDrift() in BluetoothMessageCallback()
  because we don't want to unpack the BT SMF message to update btMsgStateTime.
"""
def BluetoothTimeSyncWithDrift(btClientDeviceId, btMsgSize, btMsgId):
    """
    This method is supposed to be executed by the BT server.
    This is for time sync performed in a BT "LAN" of devices.

    Time sync between BT server and proxies.
      We assume the BT server time is correct, e.g., taken from the iCam server.
        - this function needs to be called IMMEDIATELY as a callback when
                        receiving BT message:
            - on S60 3rd we have such callback
            - on Android we can devise a separate thread that checks in the
                  BT_Received folder for new files and informs iCam.
    """

    global btMsgStateTime

    return

    DebugPrint("Entered BluetoothTimeSyncWithDrift(btClientDeviceId=%s, " \
                "btMsgSize=%d, btMsgId=%s)." % \
                (btClientDeviceId, btMsgSize, str(btMsgId)))

    # We estimate (overconservatively) the time it takes to send BT message.
    deltaBT = float(btMsgSize / 20000.0) + 10


    if SYMBIAN_S60_OS:
        try:
            # Get message time of arrival.
            btMsgArrivalTime = bluetoothInbox.time(btMsgId)
            btMsgArrivalTimeStruct = time.localtime(btMsgArrivalTime)

            """
            The problem is the BT message might have been received in another
                session of iCam, was already in the Inbox, and therefore
                BluetoothMessageCallback() was not called for btMsgId, hence
                it is not present in dictBtMsgTime[btMsgId].

            Hence we update the entry with btMsgArrivalTime.
            """
            if btMsgId not in dictBtMsgTime:
                dictBtMsgTime[btMsgId] = btMsgArrivalTime
        except:
            btMsgArrivalTime = "[ERROR]"
            btMsgArrivalTimeStruct = -1
            DebugPrintErrorTrace()

    try:
        DebugPrint(
            "    BluetoothTimeSyncWithDrift(): btMsgArrivalTime = %s, " \
            "btMsgArrivalTimeStruct = %s" % \
            (str(btMsgArrivalTime), \
            time.strftime("%Y_%m_%d_%H_%M_%S", btMsgArrivalTimeStruct)))
    except:
        DebugPrintErrorTrace()

    try:
        DebugPrint("    BluetoothTimeSyncWithDrift(): dictBtMsgTime = %s" % \
                                                            str(dictBtMsgTime))

        """
        Was giving (at least once) exception:
          Traceback (most recent call last):
            File "a.py", line 1180, in BluetoothTimeSyncWithDrift
          KeyError: 1153689
          - see Z:\1PhD\ReVival\Logs\NokiaN82\2013_12_22_2\stderr_2013_12_22_11_47_41.txt

        The reason is the BT message might have been received in another
            session of iCam, was already in the Inbox, and therefore
            BluetoothMessageCallback() was not called for btMsgId, hence it
            is not present in dictBtMsgTime[btMsgId].

        But I updated the entry - see above:
        if btMsgId not in dictBtMsgTime:
            ...
        """
        DebugPrint("    BluetoothTimeSyncWithDrift(): dictBtMsgTime[btMsgId] = %s" % \
                        str(dictBtMsgTime[btMsgId]))
    except:
        DebugPrintErrorTrace()

    try:
        """
        Note that btMsgStateTime has to be updated with the value from the
            SMF BT msg, from btMsgId.
        """
        DebugPrint("    BluetoothTimeSyncWithDrift(): btMsgStateTime = %s" % \
                        str(btMsgStateTime))

        # The BT server looks at time btMsgStateTime now and computes the time drift.
        #timeDrift = GetTime() - (btMsgStateTime + deltaBT)

        # The BT server looks at arrival time of BT msg and computes the time drift.
        #   dictBtMsgTime[btMsgId] = time of arrival of BT message
        #   btMsgStateTime = time reported in the state of the BT message.
        timeDrift = dictBtMsgTime[btMsgId] - (btMsgStateTime + deltaBT)

        DebugPrint("    BluetoothTimeSyncWithDrift(): deltaBT = %d, " \
                    "timeDrift = %d." % (deltaBT, timeDrift))

        if abs(timeDrift) > 1800: # 30 mins
            DebugPrint("    BluetoothTimeSyncWithDrift(): Running " \
                        "ExecuteCommands().")
            ExecuteCommands(
                "send-command-via-bluetooth %s " \
                "adjust-date-and-time-using-delta %d" % \
                (btAddrTable[btClientDeviceId], timeDrift))
    except:
        DebugPrintErrorTrace()


def BluetoothTimeSync():
    return
    """
    Send the current time to the BT clients:
      The info is relayed via the BT message name - the message is empty
    """
    if SYMBIAN_OS:
        try:
            #for btClientDeviceIdVal in btAddrTable.itervalues():
            for btClientDeviceIdVal in btAddrTable:
            #for btClientDeviceIdVal in [BT_ADDR_6680, BT_ADDR_6120]:
                try:
                    #crtTime = response.tx_time
                    crtTime = GetTime() + \
                                5 # We account for transmitting the msg via BT

                    pathFileName = LOCAL_FOLDER_TEMP + "/" + \
                                    BT_OBEX_FILENAME_PREFIX + \
                                    "%d" % int(crtTime) + BT_OBEX_EXTENSION_TIM

                    """
                    Requires backslashes, otherwise btsocket.bt_obex_send_file
                        gives exception: error: (22, 'Invalid argument')
                    """
                    pathFileNameWithBackslashes = \
                                            pathFileName.replace("/", "\\")

                    fOutput = open(pathFileNameWithBackslashes, "wb")
                    fOutput.close()

                    os.unlink(pathFileNameWithBackslashes)

                    BluetoothClientDiscoverServer(btClientDeviceIdVal)

                    btsocket.bt_obex_send_file(btAddrTable[btClientDeviceIdVal],
                        bluetoothServerOPPServicePort[btClientDeviceIdVal],
                        unicode(pathFileNameWithBackslashes))
                except:
                    DebugPrintErrorTrace()
        except:
            DebugPrintErrorTrace()



def TimeSyncNTP():
    DebugPrint("Entered TimeSyncNTP(): ntplib = %s." % str(ntplib))

    """
    Note that TimeSyncNTP() renders useless iCamViewer\TimeSync.py .

    Obviously, TimeSyncNTP() requires Internet connection to access NTP server.
    """
    if ntplib == None:
        return

    #DebugPrint("Entered TimeSyncNTP().")

    # Insipred from http://stackoverflow.com/questions/5222951/easy-way-to-get-the-correct-time-in-python
    try:
        client = ntplib.NTPClient()

        """
        The response is a float representing the number of seconds
                from beginning of Unix era.
        """
        #response = client.request("pool.ntp.org")
        response = client.request("europe.pool.ntp.org", version=3)

        #!!!!TODO: We should execute even faster the time setting - not sending string command to be parsed by ExecuteCommands()...;
        #!!!!TODO:    therefore make separate function SetDateTime() and call it directly and make ExecuteCommands() also call it.
        ExecuteCommands("set-date-and-time %.3f" % response.tx_time, \
                        fastExec=True)

        crtTime = GetTime()

        DebugPrint("TimeSyncNTP(): response.tx_time = %s, crtTime = %s" % \
                                        (str(response.tx_time), str(crtTime)))

        #print "time.localtime(response.tx_time) =", time.localtime(response.tx_time)
        #print "Alex: " + time.strftime('%m %d %H %M %Y.%S', time.localtime(response.tx_time))

        if False:
            BluetoothTimeSync()
    except:
        DebugPrint("TimeSyncNTP(): Could not sync with time server.")
        DebugPrintErrorTrace()



TIME_DIFFERENCE_BETWEEN_LOCAL_AND_ICAM_SERVER = 0 #3600 #0 # [seconds]
def TimeSyncWithiCamServer():
    """
    !!!!TODO: we currently don't use mobile-revival.110mb.com because it's time
        is not that reliable (+-5 min difference).
    """
    return

    DebugPrint("Entered TimeSyncWithiCamServer().")
    #"""
    if accessPointRetryConnect:
        return

    #!!!!TODO: use a serious time server like time....com (or maybe NTP)
    #!!!!TODO: measure and account for the time passed during urlopen()
    #  Get time from the iCam server
    try:
        # Look for Python runtime for Symbian, Android, etc!!!!
        iCamTimeCompressed = urllib.urlopen("http://" +
                                            ICAM_SERVER_NAME + WEB_FOLDER +
                                            "/GetTime.php").read()

        iCamTime = iCamTimeCompressed.decode("zlib")
    except:
        DebugPrintErrorTrace()
        return
    #"""
    #iCamTime = "13:00:00 28-06-2013"

    try:
        # We do not run this code, because on SYMBIAN_OS mktime() doesn't work
        if False:
            tokens = iCamTime.split(" ")
            tok2 = tokens[1].split("-")
            #(tm_year, tm_mon, tm_mday) = tok2
            tm_year = int(tok2[0])
            tm_mon  = int(tok2[1])
            tm_mday = int(tok2[2])

            tok2 = tokens[0].split(":")
            tm_hour = int(tok2[0])
            tm_min  = int(tok2[1])
            tm_sec  = int(tok2[2])

            timeReceived = (tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec, \
                                                    -1, -1, -1)
            # These last 3 parameters are tm_wday, tm_yday, tm_isdst

            #print "timeReceived =", timeReceived

            targetTime = time.mktime(timeReceived)
            #targetTime = GetTime()

        if True:
            targetTime = int(iCamTime)

        DebugPrint("TimeSyncWithiCamServer(): Received from iCam server " \
                    "targetTime = %s." % str(targetTime))

        targetTime += TIME_DIFFERENCE_BETWEEN_LOCAL_AND_ICAM_SERVER

        ExecuteCommands("set-date-and-time " + str(targetTime))
    except:
        DebugPrintErrorTrace()
        #return

###############################################################################
###############################################################################
###############################END TIME SYNC###################################
###############################################################################
###############################################################################



import httplib

"""
# Need to include it in lib(_std).zip, since it is not a standard library.
try:
    import httplib2
    import urllib3
except:
    DebugPrintErrorTrace()
"""
import urllib

sysagentImported = False
# if SYMBIAN_OS:
if SYMBIAN_S60_OS:
    try:
        import sysagent
        sysagentImported = True
    except:
        DebugPrint("Not able to import the sysagent module.")
        DebugPrintErrorTrace()

    import telephone
    import graphics
    # from key_codes import *
    import audio
    import location
    import positioning
elif SYMBIAN_UIQ_OS:
    try:
        print "Now we give import telephone."
        import telephone
        print "Calling at %s." % time.asctime(GetCurrentDateTime())
        sys.stdout.flush()
        telephone.dial(u"0770000000")
        e32.ao_sleep(17)
        telephone.hang_up()
        e32.ao_sleep(2)
    except:
        DebugPrintErrorTrace()
# import comdef

if SYMBIAN_OS:
    try:
        # pyS60VersionNumber are the first 2 digits of PyS60's version
        pyS60VersionNumber = e32.pys60_version_info[0] * 10 + \
            e32.pys60_version_info[1]
        if pyS60VersionNumber <= 14:
            # (1, 4, 5, 'final', 0)
            _PyS60_1_9_OR_NEWER = False
        elif pyS60VersionNumber >= 19:
            # (2, 0, 0, 'svn3873', 0)
            _PyS60_1_9_OR_NEWER = True
    except:
        DebugPrintErrorTrace()

    if not _PyS60_1_9_OR_NEWER:
        """
        On Python 2.2 we cannot use zip packages for module imports, so we
            replace the "lib_std.zip" with a folder with the
            uncompressed content.
        """
        sys.path[0] = os.path.join(os.getcwd(), "site-packages")

        DebugPrint("sys.path (updated) = %s" % str(sys.path))

###############################################################################
###############################################################################
##########################BEGIN S60 CAMERA MODULE IMPORT#######################
###############################################################################
###############################################################################
"""
IMPORTANT NOTE: The import of a module inside a function is visible ONLY
    inside the function - TODO!! - but we can declare as global camera, _camera2, etc.
#def ImportCameraModule():
"""
# """
camera2IsImported = False

# if SYMBIAN_OS and SYMBIAN_S60_OS:
if SYMBIAN_S60_OS:
    if _PyS60_1_9_OR_NEWER:
        if deviceId == IMEI_G810: # Was this really for G810, or was it different? I think I was just testing if loading the .pyd gave errors, which it was...
            """
            NOTE: Samsung G810 requires a different initialization in camera(2).py than
                for Nokia S60 phones - DOCUMENT BETTER!!!!TODO!!!!!!!! - see 2do_iCam_Samsung_G810.txt:
                "    the iCam was crashing at camera operations because iCam is creating more instances (for device for image_size(); for VGA camera besides Main camera) and this generates errors in ReserveComplete
                    for example 2 GeneralUseCameraS60(cameraId), without release?? crashes iCam".
                Also, it doesn't work video recording.

            IMPORTANT: It seems imp.load_dynamic() crashes PyS60 1.4.5
                without exception.
            """
            try:
                import imp

                # Gives exception "ImportError: dlopen: Load failed"
                #_camera2 = imp.load_dynamic("_camera2",
                #    "kf__camera2_e21e55ef.pyd")

                # Gives exception "ImportError: dlopen: Load failed"
                #_camera2 = imp.load_dynamic("_camera2",
                #    r"E:\sys\bin\kf__camera2_e21e55ef.pyd")

                # Gives exception "ImportError: dlopen: Load failed"
                # _camera2 = imp.load_dynamic("_camera2",
                #    r"E:\sys\bin\251__camera2.pyd")

                # Works
                # _camera2 = imp.load_dynamic("_camera", "251__camera.pyd")

                # Gives exception "ImportError: dlopen: Load failed"
                # _camera2 = imp.load_dynamic("_camera", "kf__camera_e21e55ef.pyd")

                # Gives exception "ImportError: dlopen: Load failed"
                #_camera2 = imp.load_dynamic("sysagent",
                #    "kf_sysagent_e21e55ef.pyd")

                # Not tried
                # _camera2 = imp.load_dynamic("pyinbox", "kf_pyinbox_e21e55ef.pyd")

                # _camera2 = imp.load_dynamic("camera", "kf__camera_e21e55ef.pyd")

                # Gives exception "ImportError: dlopen: Load failed"
                _camera2 = imp.load_dynamic("_camera2", "kf__camera2_e21e55ef.pyd")

                DebugPrint("Imported the _camera2.pyd module: _camera2 = %s." % \
                                                                str(_camera2))
            except ImportError:
                DebugPrint("Not able to load_dynamic the _camera2.pyd module.")
                DebugPrintErrorTrace()

        try:
            import camera2 as camera

            # import camera2
            camera2IsImported = True

            DebugPrint("Imported the camera2 module.")
        except ImportError:
            DebugPrint("Not able to import the camera2 module.")
            DebugPrintErrorTrace()

            # """
            try:
                import camera
            except:
            # except ImportError:
                DebugPrint("Not able to import the camera module.")
                DebugPrintErrorTrace()
    # For PyS60 1.4.5
    else:
        try:
            import camera
        except:
        # except ImportError:
            DebugPrint("Not able to import the camera module.")
            DebugPrintErrorTrace()
###############################################################################
###############################################################################
###########################END S60 CAMERA MODULE IMPORT########################
###############################################################################
###############################################################################

if SYMBIAN_S60_OS:
    sensorsAvailable = ""

    try:
        import sensor

        # sensorsAvailable = sensor.list_channels()
        sensorsAvailable = sensor.sensors()
        """
        Both N95, PyS60 1.4.5 and 2.0 and N82, PyS60 2.0 give
        sensorsAvailable = {u'RotSensor': {'category': 321, 'id': 12350},
            u'AccSensor': {'category': 268505087, 'id': 271003684}}
        From http://wiki.forum.nokia.com/index.php/S60_Sensor_Framework
            "Note that the old sensor API plug-ins are not compatible with
            the S60 Sensor Framework. In addition, applications created with
            Sensor FW do not work in S60 3rd Edition, FP1 devices other than
            the Nokia E66 mobile device. If you wish to provide just one
            installation file, you can pack the sensor API parts into an ECom
            plug-in, build one file with the S60 5th Edition SDK and another
            with the S60 3rd Edition MR SDK, and then load the correct one
            during runtime."
        """
    except:
        DebugPrint("Not able to import the sensor module.")
        DebugPrintErrorTrace()

    # Not available on PyS60 1.4.5 which uses Py 2.2.2 - available from Py 2.3.
    # import datetime

    try:
        import pyinbox
    except:
        DebugPrint("Not able to import the pyinbox module.")
        DebugPrintErrorTrace()

try:
    import socket
except:
    DebugPrint("Not able to import the socket module.")
    DebugPrintErrorTrace()

"""
To have socket timeout on PyS60 1.4.5 see for ex
    http://discussion.forum.nokia.com/forum/showthread.php?72774-socket-timeout
Use maybe Tsocket extension - see also
    http://discussion.forum.nokia.com/forum/showthread.php?109520-announce-tsocket-socket-module-timeout-on-connect.

Note: at least at the first Internet access, iCam running at least on Nokia E7
    seems to wait somewhat longer than the specified SOCKET_DEFAULT_TIMEOUT
    (so SOCKET_DEFAULT_TIMEOUT doesn't seem to be a hard deadline).
"""
SOCKET_DEFAULT_TIMEOUT = 20.0
# SOCKET_DEFAULT_TIMEOUT = 10.0

# Gives many timeouts, at least when the server was down:
# socket.setdefaulttimeout(5.0)
try:
    # Inspired from https://groups.google.com/group/comp.lang.python/browse_thread/thread/ff84e7340988c168/
    # socket.setdefaulttimeout(5.0)
    """
    From http://docs.python.org/library/socket.html#socket.setdefaulttimeout:
        "A value of None indicates that new socket objects have no timeout.
        When the socket module is first imported, the default is None."
    Also note that on Windows it seems that the default timeout is
        20-30 seconds.
    """
    socket.setdefaulttimeout(SOCKET_DEFAULT_TIMEOUT)
except:
    DebugPrint("Not able to use socket.setdefaulttimeout() - maybe because " \
                "running on PyS60 1.4.5.")
    DebugPrintErrorTrace()

misoIsImported = False
# if SYMBIAN_OS:
if SYMBIAN_S60_OS:
    if _PyS60_1_9_OR_NEWER:
        try:
            # Not supported in PyS60 v1.4.5, where it will generate an
            #    ImportError exception.
            import btsocket
        except:
            DebugPrint("Cannot import module btsocket - maybe because you " \
                        "are using PyS60 v1.4.5.")
            DebugPrintErrorTrace()
    else:
        # On PyS60 1.4.x, socket contained the BT functionality also.
        import socket as btsocket

    try:
        import miso
        misoIsImported = True
    except:
        DebugPrint("Not able to import the miso module.")
        DebugPrintErrorTrace()

    try:
        import inbox
        inboxIsImported = True
    except:
        inboxIsImported = False

        (exceptionTypeG, exceptionValueG, exceptionTracebackG) = sys.exc_info()

        myTextGlobal = "Cannot import inbox - details: %s." % \
                repr(traceback.format_tb(exceptionTracebackG))
        # if MY_DEBUG_UPLOAD_MSG:
        #    UploadGZippedData(deviceId, myTextGlobal, ICAM_SERVER_NAME,
        #                           WEBPAGE_UL_GZIPPED_TEXT, None)
        DebugPrint(myTextGlobal)
        DebugPrintErrorTrace()

    import messaging
###############################################################################
###############################################################################
###############################################################################
###############################END IMPORTS#####################################
###############################################################################
###############################################################################

###############################################################################
###############################################################################
###############################################################################
################################STRINGS########################################
###############################################################################
###############################################################################
"""
See http://www.daniweb.com/forums/thread138449.html and
    http://bytes.com/topic/python/answers/768072-nameerror-global-name-addbook-not-defined
"""
# pauseIntervalStr = None
# photoResolutionStr = None
# photoModeStr = None
# flashStr = None
# exposureStr = None
# whiteBalanceStr = None
pauseIntervalStr = [
        ["0 sec (Burst mode)", 0],
        ["1 sec", 1],
        ["5 sec", 5],
        ["10 sec", 10],
        ["15 sec", 15],
        ["20 sec", 20],
        ["30 sec", 30],
        ["45 sec", 45],
        ["1 min", 60],
        ["2 min", 120],
        ["3 min", 180],
        ["4 min", 240],
        ["5 min", 300],
        ["7 min", 420],
        ["10 min", 600],
        ["15 min", 900],
        ["20 min", 1200],
        ["30 min", 1800],
        ["45 min", 2700],
        ["50 min", 3000],
        ["1 hr", 3600],
        ["2 hr", 7200],
        ["3 hr", 10800],
        ["4 hr", 14400],
        ["5 hr", 18000],
        ["6 hr", 21600],
        ["10 hr", 36000],
        ["12 hr", 43200],
        ["24 hr", 86400]
    ]
photoResolutionStr = [
        ["Do Not Upload Photo", (0, 0)],
        ["Use Local Resolution", (-1, -1)],
        ["40x30", (40, 30)],
        ["80x60", (80, 60)],
        ["160x120", (160, 120)],
        ["240x180", (240, 180)],
        ["320x240", (320, 240)],
        ["400x300", (400, 300)],
        ["512x384", (512, 384)],
        ["640x480", (640, 480)],
        ["1152x864", (1152, 864)],
        ["1280x960", (1280, 960)],
        ["1600x1200", (1600, 1200)],
        ["2048x1536", (2048, 1536)],
        ["2592x1456", (2592, 1456)],
        ["2592x1944", (2592, 1944)],
        ["3264x1832", (3264, 1832)],
        ["3264x2448", (3264, 2448)],
        ["4000x2248", (4000, 2248)],
        ["4000x3000", (4000, 3000)]
    ]
photoModeStr = [
                ["RGB12", "RGB12"],
                ["RGB16", "RGB16"],
                ["RGB24", "RGB"],
                ["JPEG_Exif", "JPEG_Exif"],
                ["JPEG_JFIF", "JPEG_JFIF"]
               ]
flashStr = [
            ["Auto", "auto"],
            ["None", "none"],
            ["Red_eye_reduce", "red_eye_reduce"],
            ["Forced", "forced"],
            ["Fill_in", "fill_in"]
           ]

exposureStr = [
                ["Auto", "auto"],
                ["Center", "center"],
                ["Backlight", "backlight"],
                ["Night", "night"]
              ]

whiteBalanceStr = [
    ["Auto", "auto"],
    ["Daylight", "daylight"],
    ["Cloudy", "cloudy"],
    ["Tungsten", "tungsten"],
    ["Fluorescent", "fluorescent"],
    ["Flash", "flash"]
  ]
###############################################################################
###############################################################################
###############################################################################
##############################END STRINGS######################################
###############################################################################
###############################################################################

###############################################################################
###############################################################################
###############################################################################
############################MOTION DETECTION###################################
###############################################################################
###############################################################################

MAX_NUM_HOTSPOTS = 5
hotspot = []
# BEFORE_MAIN: For modularity we execute here!!
for myI in range(MAX_NUM_HOTSPOTS):
    hotspot += [[[0, 0], [0, 0]]]


def SetMotionDetectionHotspot(hotspotIndex,
                              percentsUL_X, percentsUL_Y,
                              percentsLR_X, percentsLR_Y):
    global hotspot, MAX_NUM_HOTSPOTS

    if hotspotIndex >= MAX_NUM_HOTSPOTS:
        return

    hotspot[hotspotIndex][0][0] = percentsUL_X / 100.0
    hotspot[hotspotIndex][0][1] = percentsUL_Y / 100.0
    hotspot[hotspotIndex][1][0] = percentsLR_X / 100.0
    hotspot[hotspotIndex][1][1] = percentsLR_Y / 100.0


# BEFORE_MAIN: For modularity we execute here!!
SetMotionDetectionHotspot(0, 0.0, 0.0, 100.0, 100.0)
SetMotionDetectionHotspot(1, 50.0, 50.0, 80.0, 80.0)

"""
numHotspots = 1
differentPixelsPercentageThreshold = [5.0]
"""
# numHotspots = 2
numHotspots = 1
differentPixelsPercentageThreshold = [5.0, 10.0]
sizeMotionCheckWindow = (40, 30)


def DetectMotion(aPrevVFFrame, aCrtVFFrame):
    # See http://wiki.forum.nokia.com/index.php/Motion_detection_with_camera
    global numHotspots, hotspot, differentPixelsPercentageThreshold, \
        sizeMotionCheckWindow

    try:
        # for hotspotIndex in range(0, numHotspots):
        for hotspotIndex in range(numHotspots):
            (crtWidth, crtHeight) = aCrtVFFrame.size

            # Draw a red rectangle.
            #aCrtVFFrame.rectangle([ (hotspot[hotspotIndex][0][0] * crtWidth,
            #   hotspot[hotspotIndex][0][1] * crtHeight),
            #   (hotspot[hotspotIndex][1][0] * (crtWidth - 1),
            #   hotspot[hotspotIndex][1][1] * (crtHeight - 1))], 0xff0000)

            #aCrtVFFrame.rectangle([ (hotspot[hotspotIndex][0][0] * crtWidth,
            #   hotspot[hotspotIndex][0][1] * crtHeight),
            #   (hotspot[hotspotIndex][1][0] * (crtWidth - 1),
            #   hotspot[hotspotIndex][1][1] * (199 - 1))], 0xff0000)

            crtStepX = int(crtWidth / sizeMotionCheckWindow[0])
            crtStepY = int(crtHeight / sizeMotionCheckWindow[1])

            #differentPixelsThreshold = crtWidth * crtHeight * \
            #   differentPixelsPercentageThreshold[hotspotIndex] / 100.0

            differentPixelsThreshold = sizeMotionCheckWindow[0] * \
                        sizeMotionCheckWindow[1] * \
                        differentPixelsPercentageThreshold[hotspotIndex] / 100.0

            numDifferentPixels = 0

            for y in range(int(hotspot[hotspotIndex][0][1] *
                                    crtHeight), int(hotspot[hotspotIndex][1][1] *
                                    (crtHeight - 1)), crtStepY):
                for x in range(int(hotspot[hotspotIndex][0][0] * crtWidth),
                                int(hotspot[hotspotIndex][1][0] *
                                (crtWidth - 1)), crtStepX):
                    (r, g, b) = aCrtVFFrame.getpixel((x, y))[0]
                    (prevr, prevg, prevb) = aPrevVFFrame.getpixel((x, y))[0]
                    delta = abs(r - prevr) + abs(g - prevg) + abs(b - prevb)

                    if delta > 3 * 10: # 3 * 30):
                        numDifferentPixels += 1
                        if numDifferentPixels > differentPixelsThreshold: #40
                            DebugPrint("DetectMotion(): detected motion - " \
                                      "numDifferentPixels = %d > " \
                                      "differentPixelsThreshold = %d." % \
                                      (numDifferentPixels,
                                         int(differentPixelsThreshold)))
                            return True

                    """
                    From http://discussion.forum.nokia.com/forum/showthread.php?114751-Is-there-a-setpixel-for-image-objects
                        (a bit also http://discussion.forum.nokia.com/forum/showthread.php?120004-How-to-access-pixel-array-of-image-captured-by-viewfinder)
                    """
                    #aCrtVFFrame.point( (x, y), newRGB )

        DebugPrint("DetectMotion(): did NOT detect motion - " \
              "numDifferentPixels = %d <= differentPixelsThreshold = %d." % \
              (numDifferentPixels, int(differentPixelsThreshold)))

        """
        # Before it was 160 x 120 ; (1152, 864)
        im = camera.take_photo('RGB', (320,240))

        # Red outline
        im.rectangle([(10,10),(40,40)], 0xff0000)

        # No code for this square
        im.rectangle([(120,10),(150,40)], 0xff0000)

        # Check hot spot whether active
        box = Image.new((30,30), 'L')  # gray scale
        box.blit(im, (10,10,40,40))
        data = getdata(box, 8)

        # Check difference for motion
        pixdiff = 0
        for x,y in zip(data, last1):
            # pix threshold 15/256
            if abs(ord(x) - ord(y)) > 15:
                pixdiff += 1
                # img threshold 90/900
                if pixdiff > 90:
                    im.rectangle([(10,10),(40,40)], fill=0xff0000) # fill
                    # Motion detected
                    break
        last1 = data

        # Show camera
        c.blit(im, (0,0), (8,12))

        #miso.reset_inactivity_time()
        """

        return False
    except:
        DebugPrintErrorTrace()

        return False


###############################################################################
###############################################################################
###############################################################################
#########################END MOTION DETECTION##################################
###############################################################################
###############################################################################

def RestartPhone():
    try:
        """
        It seems in Python CE stderrFile.close() (or maybe stdoutFile.close())
            gives exception
            "script.py - 34 <type 'exceptions.IOError'>: [Errno 0] Error" .
        """
        if WINDOWS_CE_OS_PYTHONCE == False:
            if MY_DEBUG_STDOUT:
                print "Entered RestartPhone()."
                # if deviceId != IMEI_6680:
                sys.stdout.flush()
                stdoutFile.close()

            if MY_DEBUG_STDERR:
                # sys.stderr.flush()
                stderrFile.close()

        if SYMBIAN_OS:
            """
            try:
                # This requires PowerMgmt capabilities:
                miso.restart_phone()
            except:
                DebugPrintErrorTrace()

            # In case miso.restart_phone() doesn't work (either because miso
            #    library is not loaded, or because we don't have capabilities),
            #    use the alternative solution to restart phone.
            """

            if S60_EDITION[0] >= 3:
                # This is for S60 3rd+ edition.
                e32.start_exe(u"z:\\sys\\bin\\starter.exe", "")
            else:
                """
                See http://discussion.forum.nokia.com/forum/showthread.php?108606-Reboot-device&ticket=ST-25966-eybpBkRcw9c1vkmQnQyzjYZSb6pEUFWftwA-20:
                Works on S60 2nd edition (e.g., Nokia 6680).
                """
                e32.start_exe(u"Z:\\System\\Programs\\Starter.exe", "")
        elif ANDROID_OS:
            pass
        elif iOS_PYOBJC:
            # Restarts the springboard.
            subprocess.call(["/usr/bin/restart"])
            #, stdout=fOutput, stderr=None)
        elif WINDOWS_OS:
            # Reboots
            # /sbin/reboot
            # pass
            pass
        elif WINDOWS_CE_OS_PYTHONCE:
            # WinSpawn(r"\Storage Card\iCam\Restart.exe", [])
            tmpPathFileName = LOCAL_FOLDER + "/Restart.exe"
            # I think we require backslashes
            tmpPathFileNameWithBackslashes = tmpPathFileName.replace("/", "\\")
            WinSpawn(tmpPathFileNameWithBackslashes, [])
    except:
        DebugPrintErrorTrace()


###############################################################################
###############################################################################
###############More info about firmware types: ################################
##########Z:\1PhD\ReVival\1111PyS60_extensions\cyke642\firmware.py#############
###############################################################################
###############################################################################

def GetPhoneModel():
    DebugPrint("Entered GetPhoneModel().")

    # if SYMBIAN_OS:
    if SYMBIAN_S60_OS:
        """
        Nokia6680 : sw_version = V 5.04.07 15-02-06 RM-36 (c) NOKIA
        NokiaN95  : sw_version = V 30.0.015 15-07-08 RM-159 N95(c)NMP
        NokiaN95 2: sw_version = V 35.0.002 18-11-09 RM-159 N95(c)NMP.
            os_version = (2, 0, 1540). S60 version info = (3, 1).
        Nokia6120 : sw_version = V 06.01 02-09-08 RM-243
        NokiaN82  : (from *#0000#) V 31.0.016 09-12-08 RM-313 Nokia N82 (01.01)
        NokiaC5   : sw_version = 031.022 28-Mar-2010 RM-645 (C)Nokia
        """
        try:
            """
            This gives error (I guess) if you don't have the right
                capabilities - ReadUserData??
            """
            swVersion = sysinfo.sw_version()
        except:
            DebugPrintErrorTrace()

        if swVersion.find("RM-36") != -1:
            modelName = "Nokia6680"
        elif swVersion.find("RM-84") != -1:
            modelName = "NokiaN70"
        elif swVersion.find("RM-159") != -1:
            modelName = "NokiaN95"
        elif swVersion.find("RM-170") != -1:
            modelName = "NokiaE50-1"
        elif swVersion.find("RM-208") != -1:
            modelName = "NokiaE65"
        elif swVersion.find("RM-243") != -1:
            modelName = "Nokia6120"
        elif swVersion.find("RM-313") != -1:
            modelName = "NokiaN82"
        elif swVersion.find("RM-343") != -1:
            modelName = "NokiaE66"
        elif swVersion.find("RM-356") != -1 or swVersion.find("RM-428") != -1:
            modelName = "Nokia5800"
        elif swVersion.find("RM-412") != -1:
            modelName = "NokiaE75"
        elif swVersion.find("RM-437") != -1:
            modelName = "NokiaE63"
        elif swVersion.find("RM-504") != -1:
            modelName = "Nokia5530"
        elif swVersion.find("RM-505") != -1 or swVersion.find("RM-507") != -1:
            modelName = "NokiaN97"
        elif swVersion.find("RM-530") != -1:
            modelName = "NokiaE72"
        elif swVersion.find("RM-555") != -1:
            modelName = "NokiaN97mini"
        elif swVersion.find("RM-559") != -1:
            modelName = "NokiaX6"
        elif swVersion.find("RM-588") != -1 or swVersion.find("RM-594") != -1:
            modelName = "Nokia5230"
        elif swVersion.find("RM-596") != -1:
            modelName = "NokiaN8"
        elif swVersion.find("RM-612") != -1 or swVersion.find("RM-624") != -1:
            modelName = "NokiaC6"
        elif swVersion.find("RM-626") != -1:
            modelName = "NokiaE7"
        elif swVersion.find("RM-632") != -1:
            modelName = "NokiaE5"
        elif swVersion.find("RM-645") != -1:
            modelName = "NokiaC5"
        elif swVersion.find("RM-675") != -1:
            modelName = "NokiaC7"
        elif swVersion.find("RM-697") != -1 or swVersion.find("RM-719") != -1:
            modelName = "NokiaC5-03"
        elif swVersion.find("RM-697") != -1 or swVersion.find("RM-719") != -1:
            modelName = "NokiaC5-03"
        elif swVersion.find("SGH-G810") != -1:
            # G810XDIB1 2009-02-11 SGH-G810 (c)Samsung
            modelName = "SamsungSGH-G810"
        else:
            modelName = ""
            modelFileName = "Z:/resource/versions/model.txt"

            try:
                if os.path.isfile(modelFileName):
                    """
                    #Test if modelFileName exists.
                    fInput = open(modelFileName, "rb")
                    fInput.close()
                    """

                    """
                    #"This file exist only on 3rd ed onwards"
                    #  (http://discussion.forum.nokia.com/forum/showthread.php?t=100115)
                    fInput = open("Z:/resource/versions/model.txt", "rb")
                    #fInput = open("Z:/resource/versions/model.txt", "r")
                    res = fInput.read()
                    fInput.close()

                    DebugPrint(res)

                    resF = res[2:].encode('ascii', 'ignore')

                    DebugPrint(resF)
                    """

                    import codecs

                    # We use "utf-16" encoding:
                    fInput = codecs.open(modelFileName, "r", "utf-16")

                    for myLine in fInput:
                        # print str(line)
                        modelName = str(myLine)

                    if modelName.startswith("Nokia 6120"):
                        modelName = "Nokia6120"
                    elif modelName.startswith("Nokia 6680"):
                        modelName = "Nokia6680"
            except:
                modelName = "ModelUnknown"
                DebugPrintErrorTrace()
    elif SYMBIAN_UIQ_OS:
        #import unicodedata
        #unicodedata.normalize("NFKD", title).encode("ascii", "ignore")
        #import shutil
        #shutil.copy2("Z:/resource/versions/model.txt", "E:/model.txt")
        modelName = "UIQ-model"
    elif ANDROID_OS:
        # myDroid.makeToast(myDroid.getPhoneType().result)
        modelName = str(myDroid.getPhoneType().result)
    elif iOS_PYOBJC:
        modelName = "iPhone-model"
    elif WINDOWS_OS:
        modelName = "WindowsPC"
    elif WINDOWS_CE_OS_PYTHONCE:
        modelName = "WindowsMobile"
    elif UNIX_OS:
        modelName = "LinuxPC"
    elif RASPBIAN_OS:
        modelName = "Raspberry " #!!!!TODO: use GPIO to get the rev model of RPi
        """
        From http://raspi.tv/2014/rpi-gpio-quick-reference-updated-for-raspberry-pi-b
          (see also http://raspi.tv/2013/rpi-gpio-basics-2-how-to-check-what-pi-board-revision-you-have )
        # What Raspberry Pi revision are we running?
        #  0 = Compute Module, 1 = Rev 1, 2 = Rev 2, 3 = Model B+
        """
        #print "GPIO.RPI_REVISION = %s" % str(GPIO.RPI_REVISION)
        import RPi.GPIO as GPIO
        if GPIO.RPI_REVISION == 3:
            modelName += "model B+"
        elif GPIO.RPI_REVISION == 2:
            modelName += "model B"
    return modelName


def GetCurrentDateTimeStringWithMilliseconds():
    # This is localtime(time), possibly adjusted if we have an S60 2nd ed phone
    crtTime = GetCurrentDateTime()

    """
    We use crtTime2 only to compute numMilliseconds.
    """
    crtTime2 = GetTime()

    # See http://discussion.forum.nokia.com/forum/showthread.php?116978-What-is-the-time-granularity-in-Pys60 .
    numMilliseconds = (crtTime2 - int(crtTime2)) * 1000

    #fileName = time.strftime("%Y_%m_%d_%H_%M_%S", crtTime) + \
    #    ("_%03d%s" % (numMilliseconds, fileExtension))

    return time.strftime("%Y_%m_%d_%H_%M_%S", crtTime) + \
            "_%03d" % numMilliseconds


def GetCurrentDateTimeStringNice():
    """
    crtTime = GetCurrentDateTime()
    return time.strftime("%Y_%m_%d_%H_%M_%S", crtTime) + \
            ("_%03d" % numMilliseconds)
    """

    # time.asctime(GetCurrentDateTime())
    res = time.strftime("%H:%M:%S %d-%m-%Y", GetCurrentDateTime())
    #print("GetCurrentDateTimeStringNice(): res = %s" % res)
    return res
    #return time.strftime("%H:%M:%S %d-%m-%Y", GetCurrentDateTime())


def isNaN(x):
    return (type(x) is float) and (x != x)


def isFinite(x):
    return x != Infinity


def GetGSMLocation():
    global mobileCountryCode, mobileNetworkCode, locationAreaCode, cellId

    # if SYMBIAN_OS:
    if SYMBIAN_S60_OS:
        try:
            gsmLocation = location.gsm_location()
            """
            From PyS60 documentation: "Note: Location module requires
                capabilities ReadDeviceData, ReadUserData and Location."
            See http://discussion.forum.nokia.com/forum/showthread.php?117964-gsm_location%28%29-None-Issue
              for issue with Open signing for Location capability and
              returning non-Null value:
                "gsm_location() function ! gsm_location need caps Location
                    and self-signed cert has not this capability !"
                "The only requirement for this is Python should be installed
                    and the installed Python Scriptshell must be signed by the
                    Dev cert with location capability."
            """

            if gsmLocation is None:
                if cellId is None:
                    mobileCountryCode = -1
                    mobileNetworkCode = -1
                    locationAreaCode = -1
                    cellId = -1
            else:
                (mobileCountryCode, mobileNetworkCode,
                 locationAreaCode, cellId) = gsmLocation
        except:
            DebugPrintErrorTrace()
    elif SYMBIAN_UIQ_OS:
        mobileCountryCode = -1
        mobileNetworkCode = -1
        locationAreaCode = -1
        cellId = -1
    elif ANDROID_OS:
        cellInfo = myDroid.getCellLocation()

        mobileCountryCode = -1
        mobileNetworkCode = -1
        try:
            locationAreaCode = int(cellInfo.result["lac"])
        except:
            locationAreaCode = -1

        try:
            cellId = int(cellInfo.result["cid"])
        except:
            cellId = -1
    elif iOS_PYOBJC:
        # print "myDroid.readPhoneState() =", myDroid.readPhoneState()
        # print "myDroid.readLocation() =", myDroid.readLocation()
        # "Requires API Level 7." (v 2.1)
        #print "myDroid.readSignalStrengths() =", myDroid.readSignalStrengths()
        mobileCountryCode = -1
        mobileNetworkCode = -1
        locationAreaCode = -1
        cellId = -1
    elif WINDOWS_OS:
        mobileCountryCode = -1
        mobileNetworkCode = -1
        locationAreaCode = -1
        cellId = -1
    elif WINDOWS_CE_OS_PYTHONCE:
        mobileCountryCode = -1
        mobileNetworkCode = -1
        locationAreaCode = -1
        cellId = -1




def CompareTimesOfDay(timeOfDay1, timeOfDay2):
    #timeOfDay = (hour, min, sec, ...).

    # Returns True if timeOfDay1 > timeOfDay2, else False.
    if timeOfDay1[0] < timeOfDay2[0]:
        return False

    if timeOfDay1[0] > timeOfDay2[0]:
        return True

    if timeOfDay1[1] < timeOfDay2[1]:
        return False

    if timeOfDay1[1] > timeOfDay2[1]:
        return True

    if timeOfDay1[2] < timeOfDay2[2]:
        return False

    if timeOfDay1[2] > timeOfDay2[2]:
        return True


# Maybe use numMilliseconds, as well.
# Nokia 6680
if deviceId == IMEI_6680:
    dawnTimeVec = [7, 0, 0]
    duskTimeVec = [15, 59, 59]
else:
    # dawnTimeVec = [7, 0, 0]
    dawnTimeVec = [0, 0, 0]
    # duskTimeVec = [15, 59, 59]
    duskTimeVec = [23, 59, 59]


#!!!!TODO: adapt to the current cameraMode, etc and uncomment in ReactiveLoop_real()
def ModeManager():
    global videoRecordDuration
    global dawnTimeVec, duskTimeVec
    # return

    try:
        crtTime = GetCurrentDateTime()
        if CompareTimesOfDay([crtTime.tm_hour, crtTime.tm_min, crtTime.tm_sec],
            dawnTimeVec) and CompareTimesOfDay(duskTimeVec, [crtTime.tm_hour,
                                  crtTime.tm_min, crtTime.tm_sec]):
            # Day mode
            DebugPrint("ModeManager(): Day mode - calling " \
                        "SetRecordDuration(0, 0) --> take photos.")

            """
            """
            #!!!!TODO: adapt to the current cameraMode, etc
            # To avoid setting too often - it stores state on drive.
            if videoRecordDuration[0] != 0:
                SetRecordDuration(0, 0)
        else:
            # SetRecordDuration(1, 0)
            # Night mode
            DebugPrint("ModeManager(): Night mode - calling " \
                        "SetRecordDuration(0, 15) --> record video.")

            #!!!!TODO: adapt to the current cameraMode, etc
            # To avoid setting too often - it stores state on drive.
            if videoRecordDuration[0] == 0:
                SetRecordDuration(0, 15)
    except:
        DebugPrintErrorTrace()
"""
http://discussion.forum.nokia.com/forum/showthread.php?p=743894#post743894
    and http://snippets.dzone.com/posts/show/831
See also
    http://discussion.forum.nokia.com/forum/showthread.php?202489-e32.Ao_timer%28%29.after%28...%29-crashes-for-more-than-30-minutes
"""
#!!!!TODO: take out if not being used
def LongAfter(aTimer, duration, callback):
    """
    The PyS60 timer has a limitation: it can wait for at most 2147 seconds.
        Therefore, we need to call several times after() to sleep longer than
        that.

    The reason is that Symbian method CTimer::After() takes a 32-bits integer
        and it represents microseconds, and the max value of signed int is
        2147483647 --> we can keep only 2147 seconds (35 mins, 47 secs).
    """
    MAX_DURATION_ALLOWED_FOR_AFTER = 2100  # seconds

    while duration > 0:
        if duration > MAX_DURATION_ALLOWED_FOR_AFTER:
            aTimer.after(MAX_DURATION_ALLOWED_FOR_AFTER)
            duration -= MAX_DURATION_ALLOWED_FOR_AFTER
        else:
            aTimer.after(duration, callback)
            duration = 0


if iOS_PYOBJC:
    MAX_DURATION_BETWEEN_PETTING = 30 # seconds
else:
    MAX_DURATION_BETWEEN_PETTING = 30 #seconds
    #MAX_DURATION_BETWEEN_PETTING = 1  # seconds

if SYMBIAN_OS:
    # BEFORE_MAIN!!
    # if SYMBIAN_OS and SYMBIAN_S60_OS:
    sleepAndPetWatchdogTimer = e32.Ao_timer()


"""
!!!!TODO: PowerManager should return:
    return pauseIntervalPowerManaged, conserveEnergy, changedConserveEnergy
  since conserveEnergy, changedConserveEnergy are not part of state anyhow
"""
def PowerManager():
    global pauseInterval
    global conserveEnergy, changedConserveEnergy
    global uploadUnsentData


    conserveEnergyOld = conserveEnergy

    """
    EChargingStatusError              = -1,

    /// Charger not connected/uninitialized
    EChargingStatusNotConnected       = 0,

    /// Device is charging
    EChargingStatusCharging           = 1,

    /// Charger is connected, device not charging
    EChargingStatusNotCharging        = 2,

    /// Charging almost completed
    EChargingStatusAlmostComplete     = 3,

    /// Charging completed
    EChargingStatusChargingComplete   = 4,

    /// Charging continued after brief interruption
    EChargingStatusChargingContinued  = 5

    if GetChargerStatus() == 0:
        #Conserve energy
    elif GetChargerStatus() == 1,5:
        #Conserve energy somewhat.
    elif GetChargerStatus() == 2:
        #Go for maximum performance.
    elif GetChargerStatus() == 3, 4:
        pass
    """

    try:
        crtGetBatteryLevelPercentage = float(GetBatteryLevelPercentage())
        crtGetChargerStatus = GetChargerStatus()

        pauseIntervalPowerManaged = pauseInterval

        """
        # Power manage the cellphone - attempt to avoid running out of power.
        #if (crtGetBatteryLevelPercentage <= BATTERY_LEVEL_THRESHOLD( or
        #    (crtGetChargerStatus == 0):

        #if crtGetBatteryLevelPercentage <= BATTERY_LEVEL_THRESHOLD:
        """

        if deviceId == IMEI_E7:
            # For Nokia E7 we power manage even without the charger on.
            if crtGetBatteryLevelPercentage <= BATTERY_LEVEL_THRESHOLD:
                conserveEnergy = True
            else:
                conserveEnergy = False
        else:
            """
            # VERY CONSERVATIVE POLICY - gets in standby immediately when
            #    charger gets disconnected.
            if crtGetBatteryLevelPercentage <= BATTERY_LEVEL_THRESHOLD:
                conserveEnergy = True
            elif crtGetChargerStatus == 0:
                conserveEnergy = True
            """
            if crtGetBatteryLevelPercentage <= BATTERY_LEVEL_THRESHOLD / 2:
                conserveEnergy = True
            elif (crtGetChargerStatus == 0) and \
                    (crtGetBatteryLevelPercentage <= BATTERY_LEVEL_THRESHOLD):
                conserveEnergy = True
            else:
                conserveEnergy = False

        if conserveEnergy == conserveEnergyOld:
            changedConserveEnergy = False
        else:
            changedConserveEnergy = True
        """
        # Nokia 6120 and 6680 and N95
        #if (deviceId == IMEI_6120) or (deviceId == IMEI_6680) or
        #                                       (deviceId == IMEI_N95):

        # Nokia 6120 and 6680 and N95 and N82
        #if deviceId in [IMEI_6120, IMEI_6680, IMEI_N95, IMEI_N82]:

        # Nokia 6120 and 6680 and N95 and N82
        #if deviceId in [IMEI_E7, IMEI_6120, IMEI_6680, IMEI_N95, IMEI_N82]:
        """

        if conserveEnergy == True:
            """
            if True:
            !!!!Treat the else case for this if
            """

            """
            if GetBatteryLevelPercentage() <= 50.0:
                pauseInterval = 30 * 60 #30 min
            else:
            """
            pauseIntervalPowerManaged = PAUSE_INTERVAL_POWER_MANAGED
            #conserveEnergy = True
            #uploadUnsentData = 0

        if MY_DEBUG_STDOUT:
            print "PowerManager(): crtGetBatteryLevelPercentage = %f, " \
                "crtGetChargerStatus = %d --> adjusting " \
                "pauseIntervalPowerManaged = %d." % \
                (crtGetBatteryLevelPercentage,
                   crtGetChargerStatus,
                   pauseIntervalPowerManaged)

            if conserveEnergy == False:
                sys.stdout.flush()

        return pauseIntervalPowerManaged
    except:
        DebugPrintErrorTrace()


"""
SleepAndPetWatchdog(duration) changes the duty-cycle of the app
            (PowerManager() determies it), we make this simple implementation:
    - pauseInterval always remains the configured value (e.g., 240 secs)
    - we have duration = PowerManager() to adjust the duration (e.g, make
      it 10 hours if PowerManager() considers we need to conserve energy
      since we are low on battery energy)
    - we need to pet every MAX_DURATION_BETWEEN_PETTING.
      So we have a loop
        while duration > 0:
            ...
            duration -= MAX_DURATION_BETWEEN_PETTING
            ...
            # We check if power came back after a power down in order to get out of (a big) sleep.
            if powerManage:
                PowerManager()
                ...
"""
def SleepAndPetWatchdog(duration, powerManage=False):
    global sleepAndPetWatchdogTimer, pauseInterval
    global MAX_DURATION_BETWEEN_PETTING
    global modeManagerIsEnabled
    global sentBTMessageTo6680

    DebugPrint("SleepAndPetWatchdog(): Entered SleepAndPetWatchdog().")
    # duration = pauseInterval

    """
    """
    if SYMBIAN_OS:
        try:
            """
            One needs to give at least before the first after() a cancel,
                otherwise will receive "RuntimeError: Timer pending - cancel
                first"
            """
            sleepAndPetWatchdogTimer.cancel()
        except:
            DebugPrintErrorTrace()

    if powerManage:
        if modeManagerIsEnabled:
            ModeManager()
        """
        This basically makes duration = pauseInterval (or 10 hours if the
            charger is not charging).
        """
        duration = PowerManager()

        if deviceId == IMEI_N82:
            if (changedConserveEnergy == True) and (conserveEnergy == True) and \
                                                (sentBTMessageTo6680 == 0):
                # We just changed to conserveEnergy == True state
                ExecuteCommands("send-command-via-bluetooth " + BT_ADDR_6680 + \
                                " set-pause-interval %d" % \
                                PAUSE_INTERVAL_POWER_MANAGED)
                """
                To avoid losing the fact iCam sent the above BT command, if it
                    crashes, then we store in sentBTMessageTo6680 the fact it
                    sent (or not) this message. We can then use this info when
                    iCam restarts, if necessary.
                """
                sentBTMessageTo6680 = 1
                StoreState()

    try:
        while duration > 0:
            if duration > MAX_DURATION_BETWEEN_PETTING:
                if SYMBIAN_OS:
                    sleepAndPetWatchdogTimer.after(MAX_DURATION_BETWEEN_PETTING)
                elif ANDROID_OS or RASPBIAN_OS:
                    DebugPrint("SleepAndPetWatchdog(): " \
                                "MAX_DURATION_BETWEEN_PETTING = %d." % \
                                MAX_DURATION_BETWEEN_PETTING)

                    time.sleep(MAX_DURATION_BETWEEN_PETTING)
                elif iOS_PYOBJC:
                    DebugPrint("SleepAndPetWatchdog(): Before time.sleep() " \
                                "of MAX_DURATION_BETWEEN_PETTING = %d." % \
                                MAX_DURATION_BETWEEN_PETTING)

                    """
                    It crashes app when MAX_DURATION_BETWEEN_PETTING = 30,
                        around the middle, I guess.
                    """
                    time.sleep(MAX_DURATION_BETWEEN_PETTING)
                    # time.sleep(1)

                    DebugPrint("SleepAndPetWatchdog(): finished time.sleep().")
                elif WINDOWS_CE_OS_PYTHONCE:
                    DebugPrint("SleepAndPetWatchdog(): " \
                                "MAX_DURATION_BETWEEN_PETTING = %d." % \
                                MAX_DURATION_BETWEEN_PETTING)
                    time.sleep(MAX_DURATION_BETWEEN_PETTING)

                #e32.ao_sleep(MAX_DURATION_BETWEEN_PETTING)

                duration -= MAX_DURATION_BETWEEN_PETTING

                pauseIntervalOld = pauseInterval # !!!! NOT USED

                PetWatchdog()

                if powerManage:
                    """
                    In case the charger is powered after being stopped and we made
                        duration very big (to sleep a lot), we stop sleeping.
                    Also, this helps in case we change from (very) big pauseInterval
                        to a smaller pauseInterval by stopping the sleep.
                    THIS DOESN'T WORK VERY WELL IF pauseInterval is big (e.g., 1
                    hour, etc), BUT IT'S NOT A SERIOUS FLAW.
                    """
                    #if duration > PowerManager():
                    #    return

                    # 
                    # We check if power came back after a power down in order to get out of sleep.
                    PowerManager()

                    if (changedConserveEnergy == True) and (conserveEnergy == False):
                        # We just changed back to having/receiving energy

                        if deviceId == IMEI_N82:
                            if sentBTMessageTo6680 == 1:
                                """
                                  Note that it is possible that iCam is stopped
                                when power comes back and we miss this event -
                                that is why we save as part of state
                                sentBTMessageTo6680 (we could also rely on the
                                iCam server, that better checks the state of
                                the phones in the "LAN") in order to check and
                                act, if necessary, also when iCam starts.

                                !!!!This is not great since 6680 might have a
                                    different pauseInterval than 240 - create
                                    special iCam command:
                                      information-a-different-phone-charger-down
                                """
                                ExecuteCommands("send-command-via-bluetooth " + \
                                        BT_ADDR_6680 + " set-pause-interval 240")

                                sentBTMessageTo6680 = 0
                                StoreState()
                        return

                """
                #In case the PowerManager decides to make pauseInterval shorter.
                if pauseInterval < pauseIntervalOld:
                    return
                """

                #if durationOriginal - \
                #    duration % MAX_DURATION_BETWEEN_PETTING == pauseInterval
                """
                For BT client devices we should receive only BT messages with
                    commands (CMD), so this should take very little time.
                  This ensures fast responsiveness also when iCam is in
                    ~standby mode (pauseInterval is 10 hrs or more)
                """
                if bluetoothMode == 2:
                    BluetoothMessageListProcess(processJustNonSMF_BtMsgs=True)
            else:
                if SYMBIAN_OS:
                    sleepAndPetWatchdogTimer.after(duration)
                elif ANDROID_OS or RASPBIAN_OS:
                    DebugPrint("SleepAndPetWatchdog(): duration = %d." % \
                                                                    duration)
                    time.sleep(duration)
                elif WINDOWS_CE_OS_PYTHONCE:
                    DebugPrint("SleepAndPetWatchdog(): duration = %d." % \
                                                                    duration)
                    time.sleep(duration)

                # e32.ao_sleep(duration)
                duration = 0
                PetWatchdog()
    except:
        DebugPrintErrorTrace()


# Returns the amount of free RAM in bytes.
def GetFreeRAM():
    # if SYMBIAN_OS:
    if SYMBIAN_S60_OS:
        return sysinfo.free_ram()
    elif SYMBIAN_UIQ_OS:
        return 123456789
    elif ANDROID_OS:
        return 123456789
    elif iOS_PYOBJC:
        return 123456789
    elif WINDOWS_CE_OS_PYTHONCE:
        return 123456789
    elif WINDOWS_OS:
        return 123456789
    elif UNIX_OS:
        return 123456789
    elif RASPBIAN_OS:
        return 123456789



# Returns the free space in bytes on the drive aDriveStr.
def GetFreeDriveSpace(aDriveStr):
    try:
        # if SYMBIAN_OS:
        if SYMBIAN_S60_OS:
            """
            We treat sysinfo.free_drivespace() returning int (on PyS60 1.4.5,
                normally on S60 2nd edition) or pair of ints (for PyS60 2.0).
            """

            try:
                dictDrives = sysinfo.free_drivespace()
                if isinstance(dictDrives[unicode(aDriveStr)], int):
                    return dictDrives[unicode(aDriveStr)]
                elif isinstance(dictDrives[unicode(aDriveStr)], tuple):
                    """
                    This is for our altered sysinfo module that returns 2 ints
                      instead of 1, since freeSpace is 64 bits and we can have
                      drives with more than 2GB.
                    """

                    """
                    DebugPrint("GetFreeDriveSpace(aDriveStr = %s): " \
                                "(0 = %d, 1 = %d)." % (aDriveStr,
                                dictDrives[unicode(aDriveStr)][0],
                                dictDrives[unicode(aDriveStr)][1]))
                    """
                    #return dictDrives[unicode(aDriveStr)][1] +
                    #    (dictDrives[unicode(aDriveStr)][0] << 32)

                    #return dictDrives[unicode(aDriveStr)][1] |
                    #    (dictDrives[unicode(aDriveStr)][0] << 32)

                    # """
                    """
                    IMPORTANT: We require < to specify little endian and ALSO
                        no alignment (see http://docs.python.org/library/struct.html)
                    """
                    strPack = struct.pack("<ii",
                            dictDrives[unicode(aDriveStr)][1],
                            dictDrives[unicode(aDriveStr)][0])

                    """
                    IMPORTANT: We require < to specify little endian and ALSO
                        no alignment (see http://docs.python.org/library/struct.html)
                    """
                    (myVal, ) = struct.unpack("<q", strPack)

                    return myVal
                else:
                    # """
                    return -1
            except:
                #DebugPrintErrorTrace()
                return -1
        elif SYMBIAN_UIQ_OS:
            return -1
        elif ANDROID_OS:
            """
            DOESN'T WORK: return os.statvfs(aDriveStr).f_bfree
                #AttributeError: 'module' object has no attribute 'statvfs'
            """

            """
            From http://stackoverflow.com/questions/787776/find-free-disk-space-in-python-on-os-x
                (see also http://ubuntuforums.org/showthread.php?t=961505):
            """
            # s = os.statvfs('/')
            # print (s.f_bavail * s.f_frsize) / 1024

            """
            !!!!TODO: try it out
            from jnius import autoclass # move in front
            StatFs = autoclass("android.os.StatFs")

            stats = StatFs(aDriveStr)
            availableBlocks = stats.getAvailableBlocks()
            blockSizeInBytes = stats.getBlockSize()
            freeSpaceInBytes = availableBlocks * blockSizeInBytes
            """

            return -1
        elif WINDOWS_CE_OS_PYTHONCE:
            """
            # Seems not to work on WinCE
            # From http://stackoverflow.com/questions/51658/cross-platform-space-remaining-on-volume-using-python
            #   (see also http://stackoverflow.com/questions/2973480/available-disk-space-on-an-smb-share-via-python
            #   and http://bytes.com/topic/python/answers/609682-how-check-remaining-hard-drive-space-windows)

            try:
                LOCAL_DRIVE_AUX = "\\Storage Card"
                freeDriveSpace = ctypes.c_ulonglong(0)
                #ctypes.windll.kernel32.GetDiskFreeSpaceExW(ctypes.c_wchar_p(LOCAL_DRIVE),
                #    None, None, ctypes.pointer(freeDriveSpace))
                ctypes.windll.kernel32.GetDiskFreeSpaceExW(ctypes.c_wchar_p(LOCAL_DRIVE_AUX),
                    None, None, ctypes.pointer(freeDriveSpace))
                return freeDriveSpace.value
            except:
                DebugPrintErrorTrace()
                return -1
            """

            return -1
        elif iOS_PYOBJC:
            return -1
        elif WINDOWS_OS:
            # From http://stackoverflow.com/questions/51658/cross-platform-space-remaining-on-volume-using-python
            free_bytes = ctypes.c_ulonglong(0)
            ctypes.windll.kernel32.GetDiskFreeSpaceExW(
                                                ctypes.c_wchar_p(aDriveStr),
                                    None, None, ctypes.pointer(free_bytes))

            DebugPrint("GetFreeDriveSpace(): free_bytes.value = %d." % \
                                                            free_bytes.value)

            return free_bytes.value
            # return -1
        elif UNIX_OS or RASPBIAN_OS:
            """
            Note: for Unix aDriveStr doesn't really matter, 
                    so it can be a "bogus" value

            # UNIX (they say MacOS also):
            # From http://stackoverflow.com/questions/787776/find-free-disk-space-in-python-on-os-x
            #  (see also http://ubuntuforums.org/showthread.php?t=961505):
            """
            s = os.statvfs("/")
            # print (s.f_bavail * s.f_frsize) / 1024

            res = s.f_bavail * s.f_frsize

            DebugPrint("GetFreeDriveSpace(aDriveStr=%s): returning %d." % \
                                                            (aDriveStr, res))

            return res
    except:
        DebugPrintErrorTrace()
        return -1


# Putting a "quota" on free space
AT_LEAST_FREE_DRIVESPACE_TO_STOP = 100 * 1024 * 1024

def EraseOldestFilesFromFolder(aFolder, atLeastFreeDrivespaceToStop):
    if GetFreeDriveSpace(aFolder[:2]) >= atLeastFreeDrivespaceToStop:
        return

    try:
        DebugPrint("EraseOldestFilesFromFolder(): aFolder = %s." % aFolder)

        mediaFolderContent = os.listdir(aFolder)
        # sortedMediaFolderContent = sorted(mediaFolderContent)
        """
        sort() without parameters is the ONLY one that works in Python 2.2.
            (Info on sort at http://wiki.python.org/moin/HowTo/Sorting/.)
        """
        mediaFolderContent.sort()
        sortedMediaFolderContent = mediaFolderContent

        # print "sortedMediaFolderContent =", sortedMediaFolderContent

        for mediaFileName in sortedMediaFolderContent:
            pathFileName = aFolder + "/" + mediaFileName

            if os.path.isfile(pathFileName):
                try:
                    """
                    os.unlink(pathFileName)
                    #TODO!!!! maybe use MoveFileBetweenAnyDrives(srcPathFileName, dstPathFileName)
                    """
                    DebugPrint("EraseOldestFilesFromFolder(): deleting file %s." % \
                                                                pathFileName)
                    if GetFreeDriveSpace(aFolder[:2]) >= \
                                                    atLeastFreeDrivespaceToStop:
                        return
                except:
                    DebugPrintErrorTrace()
    except:
        DebugPrintErrorTrace()


def EraseOldestFilesFromFolderWithFileCountQuota(aFolder, filterString,
                                                    fileCountQuota):
    try:
        DebugPrint("EraseOldestFilesFromFolderWithFileCountQuota(): " \
                    "aFolder = %s." % aFolder)

        mediaFolderContent = os.listdir(aFolder)
        #print "mediaFolderContent =", mediaFolderContent

        if len(mediaFolderContent) <= fileCountQuota:
            return

        """
        # Cannot do in-place the removal since it upsets the iterator
        for fileName in mediaFolderContent:
            #print "fileName =", fileName
            if not fileName.startswith("stdout_"):
                print "Removing %s." % fileName
                #mediaFolderContent.remove(fileName)
                #mediaFolderContent.__delitem__(
        """
        mediaFolderContentFiltered = []
        for fileName in mediaFolderContent:
            if fileName.startswith(filterString):
                mediaFolderContentFiltered.append(fileName)
        #print "mediaFolderContentFiltered =", mediaFolderContentFiltered

        #sortedMediaFolderContent = sorted(mediaFolderContent)
        """
        sort() without parameters is the ONLY one that works in Python 2.2.
            (Info on sort at http://wiki.python.org/moin/HowTo/Sorting/.)
        """
        mediaFolderContentFiltered.sort()
        sortedMediaFolderContent = mediaFolderContentFiltered

        #print "sortedMediaFolderContent =", sortedMediaFolderContent

        for mediaFileName in sortedMediaFolderContent:
            pathFileName = aFolder + "/" + mediaFileName
            if os.path.isfile(pathFileName):
                try:
                    """
                    #os.unlink(pathFileName)
                    #TODO!!!! maybe use MoveFileBetweenAnyDrives(srcPathFileName, dstPathFileName)
                    """
                    DebugPrint("EraseOldestFilesFromFolderWithFileCountQuota(): " \
                                "deleting file %s." % pathFileName)

                    if len(mediaFolderContent) <= fileCountQuota:
                        return
                except:
                    DebugPrintErrorTrace()
    except:
        DebugPrintErrorTrace()


def EraseOldestFilesAndMessages():
    DebugPrint("Entered EraseOldestFilesAndMessages().")

    # Media - for test "."
    EraseOldestFilesFromFolder(LOCAL_FOLDER_MEDIA_FILES,
                               AT_LEAST_FREE_DRIVESPACE_TO_STOP)

    # Unsent - for test use "O:\\!\\N82\\Media"
    EraseOldestFilesFromFolder(LOCAL_FOLDER_UNSENT_FILES,
                               AT_LEAST_FREE_DRIVESPACE_TO_STOP)

    # TODO: Maybe erase BT messages older than 1 day


def DisplayDeviceId(*args):
    global deviceId

    DebugPrint("Entered DisplayDeviceId().")

    # if SYMBIAN_OS:
    if SYMBIAN_S60_OS:
        # Good
        # From http://wiki.forum.nokia.com/index.php/Python_on_Symbian/04._Basic_User_Interface.
        """
        The ONLY problems with this one is that it has a progress bar and a
            Cancel button instead of OK.
        """
        globalui.global_note(unicode("Your device ID (IMEI): "
                             + deviceId + "."), "wait")
    elif ANDROID_OS:
        DisplayNote("Your device ID (IMEI): " + deviceId + ".")


def SelectAccessPointIfNoneAvailableAndConnectToIt():
    global accessPointName, accessPointRetryConnect

    # if SYMBIAN_OS:
    if SYMBIAN_S60_OS:
        try:
            """
            From http://discussion.forum.nokia.com/forum/showthread.php?t=97881
                (Thread: avoid "select access point " dialog)
            """
            # It seems it crashes here if accessPointName = "" (??)
            accessPointList = socket.access_points()

            if MY_DEBUG_STDOUT:
                print "SelectAccessPointIfNoneAvailableAndConnectToIt(): " \
                                            "The list of Access Points is:"
                for accessPoint in accessPointList:
                    print "iapid = %d name = %s" % (accessPoint["iapid"],
                        accessPoint["name"])
                sys.stdout.flush()

            if accessPointName == u"":
                global doNotDisplayRedrawInfo
                doNotDisplayRedrawInfo = True

                try:
                    if _PyS60_1_9_OR_NEWER:
                        iapidSelected = btsocket.select_access_point()
                    else:
                        iapidSelected = socket.select_access_point()
                except:
                    # apo = socket.access_point(iapidSelected)
                    # socket.set_default_access_point(apo)
                    doNotDisplayRedrawInfo = False
                    DebugPrintErrorTrace()

                doNotDisplayRedrawInfo = False

                index = 0
                for accessPoint in accessPointList:
                    if accessPoint["iapid"] == iapidSelected:
                        break
                    else:
                        index += 1
                if (index < 0) or (index >= len(accessPointList)):
                    DebugPrint("SelectAccessPointIfNoneAvailableAndConnectToIt():" \
                                " Manually selected to be in Offline mode.")

                    accessPointName = u""
                    StoreState()
                    return

                accessPointName = accessPointList[index]["name"]

                DebugPrint("SelectAccessPointIfNoneAvailableAndConnectToIt(): "\
                        "Manually selected Acces Point with iapid = %d " \
                        "and name = %s." % (iapidSelected, accessPointName))
                """
                DebugPrint("Manually selected Acces Point iapidSelected = %s,"\
                    " apo = %s with name = %s." % (iapidSelected, apo,
                    accessPointList[iapidSelected]["name"]) #, apo["name"]
                """
            else:
                if not _PyS60_1_9_OR_NEWER:
                    iapidSelected = -1
                    for accessPoint in accessPointList:
                        if accessPoint["name"] == accessPointName:
                            iapidSelected = accessPoint["iapid"]
                            break
                    if iapidSelected == -1:
                        DebugPrint("SelectAccessPointIfNoneAvailableAndConnectToIt(): "\
                                "Was not able to find AP selected earlier in " \
                                "current list of APs. " \
                                "Going in Offline mode.")

                        accessPointName = u""

                        StoreState()

                        return

                    DebugPrint("SelectAccessPointIfNoneAvailableAndConnectToIt():"\
                            " Selected Acces Point with iapid = %d and " \
                            "name = %s." % (iapidSelected, accessPointName))

            e32.ao_yield()

            myText = "SelectAccessPointIfNoneAvailableAndConnectToIt(): " \
                        "Connecting to the Access Point %s." % accessPointName

            DebugPrint(myText)

            if MY_DEBUG_STDERR:
                sys.stderr.write(myText + "\n")
                sys.stderr.flush()

            # appuifw.note(unicode(myText), "info")
            if _PyS60_1_9_OR_NEWER:
                # Code working on PyS60 1.9, but not on 1.4.5
                """
                Strange: it seems we need to use the set_default_access_point
                    from socket and not btsocket on PyS60 1.9+
                """
                socket.set_default_access_point(accessPointName)
            else:
                # socket.set_default_access_point(accessPointList[index])
                # Code working on PyS60 1.4.5, but not on 1.9+
                # apo = socket.access_point(accessPoint["iapid"])
                apo = socket.access_point(iapidSelected)
                socket.set_default_access_point(apo)

            accessPointRetryConnect = False

            # SetMenu() #!!!!I should uncomment this.
            StoreState()

            """
            for accessPoint in accessPointList:
                #print "iapid = %d name = %s" % (accessPoint["iapid"],
                #           accessPoint["name"])

                #print "name = %s" % access_point["name"]

                if accessPoint["name"] == u"RDS":
                #if (accessPoint["name"] == u"VF Internet pe Mobil") or
                #   (accessPoint["name"] == u"Vodafone live! PRE"):

                #if accessPoint["name"] == u"Titan":

                # I was not able to use webnwalk to upload (and most likely not
                #    able to download either) data to the web.

                #if accessPoint["name"] == u"webnwalk":
                    #socket.set_default_access_point(access_point["iapid"])
                    print "Selecting Acces Point", accessPoint["name"]

                    if _PyS60_1_9_OR_NEWER:
                        #Code working on PyS60 1.9+, but not on 1.4.5
                        socket.set_default_access_point(accessPoint["name"])
                    else:
                        #Code working on PyS60 1.4.5, but not on 2.0.0
                        apo = socket.access_point(accessPoint["iapid"])
                        socket.set_default_access_point(apo) #accessPoint)

                    #btsocket.access_point(accessPoint["iapid"])
                    #apo = btsocket.access_point(accessPoint["iapid"])
                    #print "BTSocket: The IP address ", apo.ip

                    if _PyS60_1_9_OR_NEWER:
                        # Code working on PyS60 2.0.0, but not on 1.4.5
                        #   see http://docs.python.org/library/socket.html
                        ip = socket.gethostbyname( socket.gethostname() )
                        print "IP address = ", ip

                        #print "IP address = ", socket.getaddrinfo(
                        #   socket.gethostname(), None)[0][4][0]

                        #sys.stdout.flush()

                    print "Connected to access point", accessPoint
                    break
            """
        except:
            # accessPointName = u""
            accessPointRetryConnect = True
            DebugPrintErrorTrace()

    elif SYMBIAN_UIQ_OS:
        accessPointName = u"[DEFAULT_AP]"
        """
        If accessPointRetryConnect is True it means that connection to the
            AP accessPointName was not successful. And this leads to retrying
            connection to the AP.
        """
        accessPointRetryConnect = False
    elif ANDROID_OS:
        accessPointName = u"[DEFAULT_AP]"
        """
        If accessPointRetryConnect is True it means that connection to the
            AP accessPointName was not successful. And this leads to retrying
            connection to the AP.
        """
        accessPointRetryConnect = False
    elif RASPBIAN_OS:
        accessPointName = u"[DEFAULT_AP]"
        """
        If accessPointRetryConnect is True it means that connection to the
            AP accessPointName was not successful. And this leads to retrying
            connection to the AP.
        """
        accessPointRetryConnect = False
    elif iOS_PYOBJC:
        accessPointName = u"[DEFAULT_AP]"
        """
        If accessPointRetryConnect is True it means that connection to the
            AP accessPointName was not successful. And this leads to retrying
            connection to the AP.
        """
        accessPointRetryConnect = False
    elif WINDOWS_CE_OS_PYTHONCE:
        accessPointName = u"[DEFAULT_AP]"
        """
        If accessPointRetryConnect is True it means that connection to the
            AP accessPointName was not successful. And this leads to retrying
            connection to the AP.
        """
        accessPointRetryConnect = False
    elif WINDOWS_OS:
        accessPointName = u"[Windows_PC_Internet_Connection]"
        """
        If accessPointRetryConnect is True it means that connection to the
            AP accessPointName was not successful. And this leads to retrying
            connection to the AP.
        """
        accessPointRetryConnect = False
    elif UNIX_OS:
        accessPointName = u"[Unix_PC_Internet_Connection]"
        """
        If accessPointRetryConnect is True it means that connection to the
            AP accessPointName was not successful. And this leads to retrying
            connection to the AP.
        """
        accessPointRetryConnect = False


def SelectAccessPoint():
    global accessPointName

    # We print on screen that pressing Cancel means no AP
    # if SYMBIAN_OS:
    if SYMBIAN_S60_OS:
        appuifw.app.title = u"Press Cancel to disable AP."

    accessPointName = u""
    SelectAccessPointIfNoneAvailableAndConnectToIt()

    if SYMBIAN_S60_OS:
        appuifw.app.title = ICAM_APP_TITLE


def TelephoneCallback(state):
    global deviceId

    # NUMBER_WANTED = "786266000"

    myText = "TelephoneCallback(): Receiving a call from number %s (call " \
                "state is %s)." % (str(state[1]), str(state[0]))

    DebugPrint(myText)
    """
    if MY_DEBUG_STDOUT:
        l = len(state[1])
        if (state[1][l - len(NUMBER_WANTED) : l - 1] == NUMBER_WANTED):
            print "Phone number matched ;) ! Retrieving command."
    """

    if MY_DEBUG_UPLOAD_MSG:
        UploadGZippedData(deviceId, myText, ICAM_SERVER_NAME,
                          WEBPAGE_UL_GZIPPED_TEXT, None)

    """
    This is done in order for the application to continue running after the
        call. :o - inspired from
        http://discussion.forum.nokia.com/forum/showthread.php?t=120891
    """
    #appuifw.note(u"TelephoneCallback(): Received a call - call state is %s." \
    #                % str(state[0]), "info")
    appuifw.note(unicode(myText), "info")

    if state[0] == telephone.EStatusRinging:
        try:
            # global myTimer
            # myTimer.cancel()
            pass
        except:
            DebugPrintErrorTrace()
        """
        !!!!What if the call happens at the beginning of the program when the
            vars are not initialized --> error
        """
        # ReactiveLoop()
        DownloadCommands()


################################UPLOADING RELATED##############################
################################UPLOADING RELATED##############################
################################UPLOADING RELATED##############################
################################UPLOADING RELATED##############################
################################UPLOADING RELATED##############################
################################UPLOADING RELATED##############################
################################UPLOADING RELATED##############################
################################UPLOADING RELATED##############################
################################UPLOADING RELATED##############################
################################UPLOADING RELATED##############################
NUM_MAX_THREADS_UPLOAD = 20
numThreadsUpload = 1

# These are crucial for Burst Photo mode
"""
To use InternetUploadBinaryDataMultiThreaded:
    InternetUploadBinaryData = InternetUploadBinaryDataMultiThreaded
    MULTITHREADED_PHOTO_BURST_MODE_UPLOAD3 = True
        (should use a dedicated flag, for consistency)
  with UDP also:
    UPLOAD_USING_UDP = True

To use InternetUploadBinaryDataStandard in single threaded mode:
    InternetUploadBinaryData = InternetUploadBinaryDataStandard
    MULTITHREADED_PHOTO_BURST_MODE_UPLOAD2 = True

To use InternetUploadBinaryDataStandard in multithreaded mode:
    InternetUploadBinaryData = InternetUploadBinaryDataStandard
    MULTITHREADED_PHOTO_BURST_MODE_UPLOAD2 = True
  There are other options also:
    - simple multithreaded wo getresponse
        MULTITHREADED_PHOTO_BURST_MODE_UPLOAD = False
        MULTITHREADED_PHOTO_BURST_MODE_UPLOAD2 = True
        # Does not call getresponse,uses TCP_NODELAY.
        HTTP_INTERNET_UPLOAD_FAST = True

    - fixed number of threads, with getresponse
        MULTITHREADED_PHOTO_BURST_MODE_UPLOAD = False
        MULTITHREADED_PHOTO_BURST_MODE_UPLOAD2 = True
        # One of them has to be true not to DownloadCommands()
        HTTP_INTERNET_UPLOAD_FAST = False
"""

# BEFORE_MAIN: We execute some code depending mostly on hardcoded settings!!
# Simple multithreaded wo getresponse
# if (deviceId == IMEI_6680) or (deviceId == IMEI_N82):
if deviceId == IMEI_E7:
    """
    #!!!!To use normally
    UPLOAD_USING_UDP = False

    MULTITHREADED_PHOTO_BURST_MODE_UPLOAD = False
    #MULTITHREADED_PHOTO_BURST_MODE_UPLOAD2 = False
    MULTITHREADED_PHOTO_BURST_MODE_UPLOAD2 = True

    MULTITHREADED_PHOTO_BURST_MODE_UPLOAD3 = False

    # Does not call getresponse, uses TCP_NODELAY.
    HTTP_INTERNET_UPLOAD_FAST = True
    """

    UPLOAD_USING_UDP = False

    # MULTITHREADED_PHOTO_BURST_MODE_UPLOAD = True
    MULTITHREADED_PHOTO_BURST_MODE_UPLOAD = False
    MULTITHREADED_PHOTO_BURST_MODE_UPLOAD2 = False  # True
    # MULTITHREADED_PHOTO_BURST_MODE_UPLOAD2 = False
    # MULTITHREADED_PHOTO_BURST_MODE_UPLOAD3 = True
    MULTITHREADED_PHOTO_BURST_MODE_UPLOAD3 = False

    # To not call getresponse, use TCP_NODELAY.
    HTTP_INTERNET_UPLOAD_FAST = False
else:
    # HTTP_INTERNET_UPLOAD_FAST = False
    UPLOAD_USING_UDP = False

    # MULTITHREADED_PHOTO_BURST_MODE_UPLOAD = True
    MULTITHREADED_PHOTO_BURST_MODE_UPLOAD = False
    MULTITHREADED_PHOTO_BURST_MODE_UPLOAD2 = False  # True
    # MULTITHREADED_PHOTO_BURST_MODE_UPLOAD2 = False
    # MULTITHREADED_PHOTO_BURST_MODE_UPLOAD3 = True
    MULTITHREADED_PHOTO_BURST_MODE_UPLOAD3 = False

    # To not call getresponse, use TCP_NODELAY.
    HTTP_INTERNET_UPLOAD_FAST = False
    # HTTP_INTERNET_UPLOAD_FAST = False

if MULTITHREADED_PHOTO_BURST_MODE_UPLOAD3:
    # NOTE: this REPLICATES the same reference list from the first element for
    #   all the other elements
    # dataToUploadAndPageOnServerForThread = [[]] * 10
    dataToUploadAndPageOnServerForThread = []
    for myI in range(NUM_MAX_THREADS_UPLOAD):
        dataToUploadAndPageOnServerForThread.append([])
    threadLock = []
    for myI in range(NUM_MAX_THREADS_UPLOAD):
        threadLock.append(None)

    UDP_PORT = 1113
    if UPLOAD_USING_UDP:
        # Inet, UDP
        socketUDP = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)


def InternetUploadThread(threadId):
    global threadLock, dataToUploadAndPageOnServerForThread

    threadLock[threadId] = e32.Ao_lock()

    httpRequestHeader = {"Connection:": "Keep-alive",
                         "Content-type": "application/x-gzip",
                         "Accept": "text/plain"}

    myHTTPConnection2 = None
    while myHTTPConnection2 is None:
        try:
            myHTTPConnection2 = httplib.HTTPConnection(ICAM_SERVER_NAME)
        except:
            DebugPrintErrorTrace()
            myHTTPConnection2 = None

    DebugPrint("InternetUploadThread(%d): time %s, thread ready to upload " \
                "packets." % \
                (threadId, GetCurrentDateTimeStringWithMilliseconds()))

    while True:
        # for i in range(len(dataToUploadAndPageOnServerForThread[threadId])):
        while len(dataToUploadAndPageOnServerForThread[threadId]) > 0:
            try:
                DebugPrint("InternetUploadThread(threadId = %d): before upload"\
                        " - time %s, len(dataToUploadAndPageOnServerForThread[threadId]) = %d." % \
                        (threadId,
                           GetCurrentDateTimeStringWithMilliseconds(),
                       len(dataToUploadAndPageOnServerForThread[threadId])))

                """
                Remove the item at the given position in the list and return it.
                    (From http://docs.python.org/tutorial/datastructures.html)
                """
                dataToUploadAndPageOnServerPair = \
                    dataToUploadAndPageOnServerForThread[threadId].pop(0)

                #dataToUploadAndPageOnServerPair = \
                #    dataToUploadAndPageOnServerForThread[threadId][i]

                if UPLOAD_USING_UDP and (dataToUploadAndPageOnServerPair[1] \
                            == WEBPAGE_UL_GZIPPED_STATE_AND_FILE) and \
                            (len(dataToUploadAndPageOnServerPair[0]) < 1460):
                    # Maybe see http://feetup.org/blog/dev/python/symbianPython.html
                    res = socketUDP.sendto(dataToUploadAndPageOnServerPair[0],
                            (ICAM_SERVER_NAME, UDP_PORT))

                    DebugPrint("InternetUploadThread(threadId = %d): after " \
                            "upload - time %s, socketUDP.sendto() " \
                            "returned res = %d." % (threadId,
                               GetCurrentDateTimeStringWithMilliseconds(),
                               res))
                else:
                    myHTTPConnection2.request("POST",
                            dataToUploadAndPageOnServerPair[1],
                            dataToUploadAndPageOnServerPair[0],
                            httpRequestHeader)

                    """
                    From http://docs.python.org/library/httplib.html:
                        "Note that you must have read the whole response
                        before you can send a new request to the server."
                    """
                    httpResponse = myHTTPConnection2.getresponse()

                    # """
                    """
                    On N82 with Linux server on the Intranet I need to give
                        httpResponse.read(), otherwise I get exception at the
                        next? call: ResponseNotReady, and then CannotSendRequest.
                    See also http://mail.python.org/pipermail/tutor/2003-May/022635.html:
                    "In order to re-use the connection, before you make the
                        next request, you need to read the data from the last
                        request. Not sure why that is, but thats the way it is.
                        So make sure you do your read.
                        Hope that helps someone else out there."
                    """
                    httpResponseString = httpResponse.read()

                    DebugPrint("InternetUploadThread(): " \
                            "httpResponseString = %s." % httpResponseString)

                    # """
                    DebugPrint("InternetUploadThread(threadId = %d): after " \
                        "upload - time %s, " \
                        "len(dataToUploadAndPageOnServerForThread[threadId]) = %d." % \
                        (threadId,
                           GetCurrentDateTimeStringWithMilliseconds(),
                           len(dataToUploadAndPageOnServerForThread[threadId])))
            except:
                DebugPrintErrorTrace()

        # if len(dataToUploadAndPageOnServerForThread[threadId]) == 0:
        threadLock[threadId].wait()

    # Close the connection.
    myHTTPConnection2.close()


# BEFORE_MAIN: We leave here these statements and not move them in Main(), in the idea that we will split the script in more modules!!
# if SYMBIAN_OS:
if MULTITHREADED_PHOTO_BURST_MODE_UPLOAD3:
    for threadIndex in range(numThreadsUpload):
        #thread.start_new_thread(InternetUploadThread, (threadIndex, ))
        MyThreadStart(InternetUploadThread, (threadIndex, ))


def InternetUploadBinaryDataMultiThreaded(dataToUpload,
        inetServerAddress, pageOnServer):
    global threadLock, dataToUploadAndPageOnServerForThread

    """
    !!!!
    IMPORTANT: Need to start explicitely
        http://mobile-revival.110mb.com/ReVival/UDP_stream_socket_server.php.
        It seems it stops sometimes by itself - there is a timeout on
        110mb.com, I guess.
    if UPLOAD_USING_UDP:
        myHTTPConnection = httplib.HTTPConnection(inetServerAddress)
        #ICAM_SERVER_NAME
        myHTTPConnection.request("GET",
        "http://mobile-revival.110mb.com/ReVival/UDP_stream_socket_server.php",
            "", httpRequestHeader)
    """

    if not SYMBIAN_OS:
        InternetUploadBinaryDataStandard(dataToUpload, inetServerAddress,
                                            pageOnServer)
        return

    DebugPrint("Entered InternetUploadBinaryData() - multithreaded version: " \
        "time %s, (accessPointRetryConnect = %d), " \
        "pageOnServer = %s, len(dataToUpload) = %d." % \
        (GetCurrentDateTimeStringWithMilliseconds(),
           accessPointRetryConnect, pageOnServer,
           len(dataToUpload)))

    """
    if UPLOAD_USING_UDP and (pageOnServer == WEBPAGE_UL_GZIPPED_STATE_AND_FILE)\
            and (len(dataToUpload) < 1460):
        res = socketUDP.sendto(dataToUpload, (ICAM_SERVER_NAME, UDP_PORT))
        DebugPrint("InternetUploadBinaryData() - multithreaded version: " \
                    "time %s, socketUDP.sendto() returned res = %d." % \
                    (GetCurrentDateTimeStringWithMilliseconds(), res))
        return 0
    """

    try:
        """
        DebugPrint("InternetUploadBinaryData() - multithreaded version: " \
                    "dataToUploadAndPageOnServerForThread = %s." % \
                    (str(dataToUploadAndPageOnServerForThread)))
        """
        minPackets = 1000000
        minPacketsIndex = -1
        for threadId in range(numThreadsUpload):
            if minPackets > len(dataToUploadAndPageOnServerForThread[threadId]):
                minPackets = len(dataToUploadAndPageOnServerForThread[threadId])
                minPacketsIndex = threadId

        DebugPrint("InternetUploadBinaryData() - multithreaded version: " \
                "minPackets = %d, minPacketsIndex = %d." % \
                (minPackets, minPacketsIndex))

        if threadLock[minPacketsIndex] is None:
            #thread.start_new_thread(InternetUploadThread, (minPacketsIndex, ))
            MyThreadStart(InternetUploadThread, (minPacketsIndex, ))

        dataToUploadAndPageOnServerForThread[minPacketsIndex].append(
                                                (dataToUpload, pageOnServer))
        threadLock[minPacketsIndex].signal()
    except:
        DebugPrintErrorTrace()

    return 0


"""
Note: every function uploading through Internet uses
    InternetUploadBinaryData().
"""
myHTTPConnection = None
if HTTP_INTERNET_UPLOAD_FAST:
    # From http://www.cmlenz.net/archives/2008/03/python-httplib-performance-problems
    realsocket = socket.socket


    def socketwrap(family=socket.AF_INET, myType=socket.SOCK_STREAM, proto=0):
        sockobj = realsocket(family, myType, proto)
        sockobj.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
        return sockobj

    socket.socket = socketwrap

    """
    We try connecting to the server until we succeed (hopefully this is
        doable otherwise we are stuck here) :)
    """
    while myHTTPConnection is None:
        try:
            myHTTPConnection = httplib.HTTPConnection(ICAM_SERVER_NAME)
        except:
            DebugPrintErrorTrace()
            myHTTPConnection = None
numThreadIssued = 0


def SaveUnsentData(dataToUpload, pageOnServer):
    fileExtension = None

    if pageOnServer == WEBPAGE_UL_GZIPPED_TEXT:
        if saveUnsentPackets != 2:
            fileExtension = EXTENSION_TEXT_MESSAGE
        else:
            fileExtension = None
    elif (pageOnServer == WEBPAGE_UL_GZIPPED_STATE_AND_FILE) or \
            (pageOnServer == WEBPAGE_UL_GZIPPED_STATE_AND_FILE_PROXY_YOUTUBE):
        fileExtension = EXTENSION_STATE_AND_MEDIA_FILE
    elif pageOnServer == WEBPAGE_UL_GZIPPED_FILE:
        # Currently not implemented.
        fileExtension = EXTENSION_ARBITRARY_FILE
        # return -1

    try:
        if fileExtension is not None:
            """
            crtTime2 = GetTime()
            # See http://discussion.forum.nokia.com/forum/showthread.php?116978-What-is-the-time-granularity-in-Pys60 .
            numMilliseconds = (crtTime2 - int(crtTime2)) * 1000
            fileName = time.strftime("%Y_%m_%d_%H_%M_%S", crtTime) + \
                        ("_%03d%s" % (numMilliseconds, fileExtension))
            """
            fileName = GetCurrentDateTimeStringWithMilliseconds() + \
                                                            fileExtension
            fOutput = open(LOCAL_FOLDER_UNSENT_FILES + "/" + fileName, "wb")
            fOutput.write(dataToUpload)
            # fOutput.flush()
            fOutput.close()
    except:
        DebugPrintErrorTrace()


#def TreatExceptionInternetUploadBinaryData(funcName):
def TreatException(funcName):
    global internetUploadErrorsCounter, accessPointRetryConnect, \
            MY_DEBUG_UPLOAD_MSG

    # Reset internetUploadErrorsCounter when successful:
    #internetUploadErrorsCounter = 0

    # !!!!Not really a good idea SelectAccessPoint() - if there is an
    #   error from the operator, we do not want to have a dialog
    #   appearing asking for selecting an Access Point.

    #SelectAccessPoint()
    #global accessPointName
    #accessPointName = u""
    myText = "%s returned exception. " \
                "Details: time = %s, free_ram = %d." % \
                (funcName, GetCurrentDateTimeStringNice(), GetFreeRAM())

    DebugPrint(myText)
    #exceptionType, exceptionValue, exceptionTraceback = sys.exc_info()
    # repr(traceback.format_tb(exceptionTraceback))

    if MY_DEBUG_STDERR:
        sys.stderr.write(myText + " (see above).\n\n")
        #traceback.print_exc()
        sys.stderr.flush()

    if ANDROID_OS:
        if internetUploadErrorsCounter == internetUploadMaxErrors - 1:
            # !!!!TODO: finish for Android
            DebugPrint("%s: internetUploadErrorsCounter became %d " \
                        "and accessPointRetryConnect = %d." % \
                        (funcName, internetUploadErrorsCounter,
                            accessPointRetryConnect))
    elif SYMBIAN_OS:
        #if internetUploadErrorsCounter == 20 - 1:
        if internetUploadErrorsCounter == internetUploadMaxErrors - 1:
            myText = "%s: internetUploadErrorsCounter became %d --> " \
                    "quitting iCam." % (funcName, internetUploadErrorsCounter)

            DebugPrint(myText)

            if MY_DEBUG_STDERR:
                sys.stderr.write(myText + "\n")
                sys.stderr.flush()

            """
            Making MY_DEBUG_UPLOAD_MSG False to avoid giving more
                errors when trying to upload the message in Quit(),
                in which case Quit doesn't progress after the
                message upload, being stuck in a recursive
                execution with errors:
            See log of N95N95N95N95N95 on June 8th, 2011:
                Exiting iCam at 07:58:50 08-06-2011 - command given
                    from the cellphone.
                ...
                Exiting iCam at 09:06:06 08-06-2011 - command given
                    from the cellphone.
                ...
            and http://mobile-revival.110mb.com/ReVival/N95N95N95N95N95/FromPhone/stdout_2011_06_08_08_45_13.txt
            """
            MY_DEBUG_UPLOAD_MSG = False

            """
            TODO!!!!: In case restarting iCam doesn't help we should restart the phone.
             - TODO: for this to work don't reset internetUploadErrorsCounter
                to 0 when iCam starts --> store internetUploadErrorsCounter in
                iCam state.
            """
            Quit()
        else:
            # !!!!Maybe not a good idea.
            DebugPrint("%s: internetUploadErrorsCounter became %d --> " \
                        "accessPointRetryConnect = %d and making it True; " \
                        "also disconnecting from access point (to attempt " \
                        "reconnect)." % \
                        (funcName, internetUploadErrorsCounter,
                           accessPointRetryConnect))

            if MY_DEBUG_STDERR:
                sys.stderr.write(myText + "\n")
                sys.stderr.flush()

            btsocket.set_default_access_point(None)
            """
            We got an exception while uploading data on the Inet,
                so we ASSUME we disconnected from the AP and try
                to connect next time.
            """
            accessPointRetryConnect = True
    elif iOS_PYOBJC:
        DebugPrint("%s: internetUploadErrorsCounter became %d and " \
                    "accessPointRetryConnect = %d." % \
                    (funcName, internetUploadErrorsCounter,
                       accessPointRetryConnect))
    elif WINDOWS_OS:
        DebugPrint("%s: internetUploadErrorsCounter became %d and " \
                    "accessPointRetryConnect = %d." % \
                    (funcName, internetUploadErrorsCounter,
                       accessPointRetryConnect))
    elif WINDOWS_CE_OS_PYTHONCE:
        DebugPrint("%s: internetUploadErrorsCounter became %d and " \
                    "accessPointRetryConnect = %d." % \
                    (funcName, internetUploadErrorsCounter,
                       accessPointRetryConnect))
    """
    if ( (deviceId != IMEI_N82) and
            (internetUploadErrorsCounter == 100) ) or #10)
         ( (deviceId == IMEI_N82) and
            (internetUploadErrorsCounter == 300) ):
        if MY_DEBUG_STDOUT:
            print "Because internetUploadErrorsCounter == %d, we call"\
                    " RestartPhone()." % internetUploadErrorsCounter
            sys.stdout.flush()
        RestartPhone()
    """
    internetUploadErrorsCounter += 1
    """
    # The user can choose an AP only if he wishes from the Application
    #   menu or from the Remote Control Panel
    if accessPointName == u"":
        SelectAccessPointIfNoneAvailableAndConnectToIt()
    """


def InternetUploadBinaryDataStandard(dataToUpload, inetServerAddress,
                                    pageOnServer, itIsASeparateThread=False):
    global myHTTPConnection
    global accessPointName, accessPointRetryConnect, bluetoothMode
    global numThreadIssued
    global useiCamServer
    global MY_DEBUG_UPLOAD_MSG

    # if WINDOWS_OS:
    """
    Currently we don't have libssl on WinCE (and not really for Windows - plus
        we test on Windows) so we use a proxy that communicates with YouTube.
      WINDOWS_OS is more for testing purposes.
    """
    if WINDOWS_CE_OS_PYTHONCE or WINDOWS_OS:
        # if itIsASeparateThread == True:
        if (pageOnServer == WEBPAGE_UL_GZIPPED_STATE_AND_FILE) and \
                                    (itIsASeparateThread == False):
            myText = "InternetUploadBinaryData(): upload the media file " \
                    "to YouTube (through proxy) and " \
                    "if useiCamServer == 2 to iCam."

            DebugPrint(myText)

            if MY_DEBUG_STDERR:
                sys.stderr.write(myText + "\n")
                sys.stderr.flush()

            InternetUploadBinaryDataStandard(dataToUpload,
                            ICAM_GAE_SERVER_NAME,
                            WEBPAGE_UL_GZIPPED_STATE_AND_FILE_PROXY_YOUTUBE,
                            itIsASeparateThread=True)
            #!!!!TODO: do better: avoid itIsASeparateThread to avoid recursion
            # InternetUploadBinaryDataStandard(dataToUpload, "localhost:8080",
            #   "/proxyyoutube", itIsASeparateThread=True)

            # return 0

            if MY_DEBUG_UPLOAD_MSG:
                UploadGZippedData(deviceId,
                                "Finished uploading media data to " \
                                "YouTube proxy at time %s - packet of size " \
                                "%d bytes." % \
                                (GetCurrentDateTimeStringWithMilliseconds(),
                                len(dataToUpload)), ICAM_SERVER_NAME,
                                WEBPAGE_UL_GZIPPED_TEXT, None)

            """
            This means we only upload the state+video to YouTube and do not
                upload anything to iCam.
            """
            if useiCamServer == 0:
                return 0

            elif useiCamServer == 1:
                """
                !!!!Do send the state, or (don't we do this already?!!!!) at least inform iCam server that you
                    uploaded to YouTube.
                We send ONLY THE STATE data from the Bluetooth message, to the
                    iCam Server.

                #stateData = stateFileData[struct.calcsize(deviceIdFormat) :
                #    struct.calcsize(deviceIdFormat) +
                #    struct.calcsize(statePackFormat)]

                stateData = stateFileData[0 :
                                            struct.calcsize(deviceIdFormat) +
                                            struct.calcsize(statePackFormat)]

                stateDataCompressed = stateData.encode("zlib")
                UploadUnsentBinaryData(btClientDeviceId, fileName,
                                        stateDataCompressed)
                """
                #pass
                #!!!!TODO We have to send the state to iCam server

                return 0

            elif useiCamServer == 2:
                pass #!!!!TODO - I think everything is OK - we just run again this function.

            """
            #inetServerAddress = "localhost:8080"
            inetServerAddress = ICAM_GAE_SERVER_NAME
            pageOnServer = "/proxyyoutube"
            """
    """
    if inetServerAddress != "localhost:8080":
        return 0
    """

    if MULTITHREADED_PHOTO_BURST_MODE_UPLOAD2:
        if itIsASeparateThread == False:
            """
            We create a separate thread to upload the data.
            We create only numThreadsUpload - 1 parallel threads to the main
                thread to upload.
            (Basically, we do this such that if numThreadsUpload == 1 we don't
                spawn any new thread.)
            """
            if numThreadIssued < numThreadsUpload - 1:
                DebugPrint("InternetUploadBinaryData(): " \
                            "itIsASeparateThread = %d, numThreadIssued = %d. "\
                            "Creating a new thread for upload." % \
                            (itIsASeparateThread, numThreadIssued))
                """
                thread.start_new_thread(InternetUploadBinaryData,
                        (dataToUpload, inetServerAddress, pageOnServer, True))
                """
                MyThreadStart(InternetUploadBinaryData,
                        (dataToUpload, inetServerAddress, pageOnServer, True))

                numThreadIssued += 1

                # e32.ao_sleep(2)
                """
                We do not report errors anymore... (being in burst mode we care
                    for performance, so we do not retry sending unsent).
                However, we still must try to reconnect to AP if got
                    disconnected!!!!
                """
                return 0

    DebugPrint("Entered InternetUploadBinaryData(): time %s, " \
        "accessPointRetryConnect = %d, inetServerAddress = %s, pageOnServer = %s, " \
        "len(dataToUpload) = %d, itIsASeparateThread = %d, " \
        "numThreadIssued = %d." % \
        (GetCurrentDateTimeStringWithMilliseconds(),
            accessPointRetryConnect,
            inetServerAddress,
            pageOnServer,
            len(dataToUpload),
            itIsASeparateThread,
            numThreadIssued))

    if accessPointRetryConnect:
        """
        We do this to avoid getting the dialog box with the Select AP.
        Unfortunately, it seems that at least on N95 when the phone's Inet AP
            doesn't work, trying to reconnect to it doesn't really work...
        """
        SelectAccessPointIfNoneAvailableAndConnectToIt()

    res = 0

    if NoInternetConnection():  # and bluetoothMode != 2):
        """
        When in offline mode (accessPointName == u"") I save data in Unsent.
        Otherwise, if not uploading, nor storing for later, one can just stop
            iCam (we assume for the moment iCam is not processing the video
            info and cannot trigger a notification via SMS/MMS or call).
        """
        DebugPrint("InternetUploadBinaryData(): not uploading data now; " \
                    "deferring it for when we have Internet access.")
        res = -1
        """#!!!!TODO: think why
        elif useiCamServer == 0:
        DebugPrint("InternetUploadBinaryData(): not uploading data since " \
                    "useiCamServer == 0.")
        res = -1
        """
    else:
        """
        Tell the HTTP command we are sending a gz file (normally) and we want
            text in the response.
        """
        httpRequestHeader = {"Connection:": "Keep-alive",
                             "Content-type": "application/x-gzip",
                             "Accept": "text/plain"}
        """
        From http://forums.devshed.com/python-programming-11/httplib-urllib-how-do-i-keep-a-connection-open-142565.html:
            "there is a Connection: Keep-alive option that you can pass in the
            header, if you use HTTP/1.1 protocol."
        """
        try:
            """
            if HTTP_INTERNET_UPLOAD_FAST == False:
                myHTTPConnection = httplib.HTTPConnection(inetServerAddress)
            """
            myHTTPConnection = httplib.HTTPConnection(inetServerAddress)

            if SYMBIAN_UIQ_OS:
                DebugPrint("InternetUploadBinaryData(): returned " \
                            "from httplib.HTTPConnection().")
            """
            !!!!len(dataToUpload) might be big (> 1MB), and it is not good to
                read all data in RAM at once. So do buffered reading and
                sending.!!!!
            From http://docs.python.org/library/httplib.html.
            HTTPConnection.request(method, url[, body[, headers]])
            This will send a request to the server using the HTTP request
                method method and the selector url.
            If the body argument is present, it should be a string of data to
                send after the headers are finished.
            !!!!Alternatively, it may be an open file object, in which case the
                contents of the file is sent; this file object should support
                fileno() and read() methods.
            The header Content-Length is automatically set to the correct value.
            The headers argument should be a mapping of extra HTTP headers to
                send with the request.
            """
            myHTTPConnection.request("POST", pageOnServer,
                    dataToUpload, httpRequestHeader)

            if SYMBIAN_UIQ_OS:
                DebugPrint("InternetUploadBinaryData(): returned from " \
                            "myHTTPConnection.request().")

            if SYMBIAN_OS:
                """
                DebugPrint("InternetUploadBinaryData(): before calling " \
                            "e32.ao_yield().")
                """

                """
                This has the effect of flushing the eventual pending UI events.
                From [Mobile_Python_2007]: "The e32.ao_yield() at the end of
                    the loop makes sure that the system leaves some time to
                    register the keyboard events, as drawing in the tight loop
                    consumes lots of CPU power and might make the system
                    unresponsive."

                From PyS60 2.0 documentation: Yields to the active scheduler to
                    have ready active objects with priority above normal
                    scheduled for running. This has the effect of flushing the
                    eventual pending UI events. Note that the UI callback code
                    may be run in the context of the thread that performs an
                    ao_yield. For information on active scheduler, see S60 SDK
                    documentation [4].
                """
                # e32.ao_yield()
                pass

                """
                DebugPrint("InternetUploadBinaryData(): after calling " \
                            "e32.ao_yield().")
                """

            """
            From http://docs.python.org/library/httplib.html:
            "Note that you must have read the whole response before you can
                send a new request to the server."
            But I am not doing this...
            """

            """
            # Get the response.
            # From http://docs.python.org/library/httplib.html:
            #   "Note that you must have read the whole response before you can
            #   send a new request to the server."
            httpResponse = myHTTPConnection.getresponse()
            """

            if HTTP_INTERNET_UPLOAD_FAST == False:
                # Get the response.
                # From http://docs.python.org/library/httplib.html: "Note that
                #   you must have read the whole response before you can send
                #   a new request to the server."
                httpResponse = myHTTPConnection.getresponse()

                """
                I don't really believe this info from 2003 is valid now:
                    From http://mail.python.org/pipermail/tutor/2003-May/022635.html:
                    "In order to re-use the connection, before you make the
                    next request, you need to read the data from the last
                    request. Not sure why that is, but thats the way it is.
                    So make sure you do your read.
                    Hope that helps someone else out there."
                """
                httpResponseString = httpResponse.read()
                DebugPrint("InternetUploadBinaryData(): " \
                        "httpResponseString = %s." % httpResponseString)

                # Close the connection.
                myHTTPConnection.close()
            """
            else:
                # Close the connection - I don't like to leave so many
                #   connections open... :)
                myHTTPConnection.close()
            """
        except:
            DebugPrintErrorTrace()
            TreatException("InternetUploadBinaryData()")
            res = -1

    # Save data in Unsent.
    if (res == -1) and (saveUnsentPackets > 0):
        SaveUnsentData(dataToUpload, pageOnServer)

    if MULTITHREADED_PHOTO_BURST_MODE_UPLOAD2:
        if itIsASeparateThread == False:
            """
            We have one main thread for upload and the other are parallel to
                this one. (Basically, we do this such that if
                numThreadsUpload == 1 we don't spawn any new thread.)
            """
            if numThreadIssued > 0:
                numThreadIssued -= 1

    return res


InternetUploadBinaryData = InternetUploadBinaryDataStandard


def AddPacketHeader(rawData, footer=False):
    global deviceId
    """
    IMPORTANT: We require < to specify no alignment
        (see http://docs.python.org/library/struct.html)
    """
    if footer == True:
        return rawData + struct.pack(deviceIdFormat, deviceId)
    else:
        return struct.pack(deviceIdFormat, deviceId) + rawData


# InternetUploadGZippedData() is used to send media files and text messages.
def InternetUploadGZippedData(aDeviceId, uncompressedData, inetServerAddress,
                                pageOnServer):
    global accessPointName
    global LOCAL_FOLDER_UNSENT_FILES
    global EXTENSION_TEXT_MESSAGE, EXTENSION_STATE_AND_MEDIA_FILE, \
        EXTENSION_ARBITRARY_FILE
    global WEBPAGE_UL_GZIPPED_TEXT, WEBPAGE_UL_GZIPPED_STATE_AND_FILE, \
        WEBPAGE_UL_GZIPPED_FILE

    try:
        DebugPrint("Entered InternetUploadGZippedData(%s): accessPointName = %s, " \
                "pageOnServer = %s, len(uncompressedData) = %d." % \
                (aDeviceId, accessPointName, pageOnServer,
                   len(uncompressedData)))

        # Add deviceId identifier in front of uncompressedData.
        #uncompressedData = struct.pack("100s", aDeviceId) + uncompressedData
        #    + "\n"

        #uncompressedData = struct.pack("100s", aDeviceId) + uncompressedData
        uncompressedData = AddPacketHeader(uncompressedData)

        # Compress the data.
        compressedData = uncompressedData.encode("zlib")

        return InternetUploadBinaryData(compressedData,
                inetServerAddress, pageOnServer)
    except:
        DebugPrint("InternetUploadGZippedData exception.")
        DebugPrintErrorTrace()
        return -1


def UploadBinaryData(dataToUpload, inetServerAddress, inetPageOnServer,
            fileName=None): # fileName is currently used ONLY for media files and FIL?? packets.

    global MY_DEBUG_STDOUT, bluetoothMode, accessPointName, \
        bluetoothServerAddress

    PetWatchdog()

    myText = "UploadBinaryData(): len(dataToUpload) = %d (size of data " \
                "packet sent in bytes), inetServerAddress = %s, " \
                "inetPageOnServer = %s, fileName = %s." \
                % (len(dataToUpload), inetServerAddress, inetPageOnServer,
                   fileName)

    if inetPageOnServer == WEBPAGE_UL_GZIPPED_STATE_AND_FILE:

        # BT client
        if bluetoothMode == 2:
            """
            UploadUnsentFILES() #Attempting to send the unsent media files, as
                well, before sending the current media file.
            """
            myText += "Uploading to the Bluetooth server only."
            DebugPrint(myText)
            if MY_DEBUG_STDERR:
                sys.stderr.write(myText + "\n")
                sys.stderr.flush()

            return BluetoothUploadBinaryData(bluetoothServerAddress,
                    dataToUpload, fileName)
        else:
            """
            It should ALWAYS hold: aDeviceId == deviceId. Impersonating a
                different phone is no longer necessary - I was using this
                before, to relay a message.
            """

            # First condition is to avoid calling very often
            #   UploadUnsentFILES() and 2nd is to check if it is the BT server.
            #if (not pathFileName.startswith(LOCAL_FOLDER_UNSENT_FILES) and
            #   bluetoothMode == 1):
            # If the phone is standalone (not BT) or BT server.
            if bluetoothMode == 0 or bluetoothMode == 1:
                # Attempting to send the unsent files, as well, before sending
                #   the current data.
                UploadUnsentFILES()

            myText += "Uploading via Internet."
            DebugPrint(myText)

            if MY_DEBUG_STDERR:
                sys.stderr.write(myText + "\n")
                sys.stderr.flush()

            res = InternetUploadBinaryData(dataToUpload, inetServerAddress,
                                            inetPageOnServer)

            DebugPrint("UploadBinaryData(): InternetUploadBinaryData " \
                        "returned %d." % res)

            return res

    elif inetPageOnServer == WEBPAGE_UL_GZIPPED_TEXT:

        # BT client
        if bluetoothMode == 2:
            """
            Attempting to send the unsent media files, as well, before
                sending the current media file.
            """
            # UploadUnsentFILES()
            return BluetoothUploadBinaryData(bluetoothServerAddress,
                    dataToUpload, None)
        else:
            return InternetUploadBinaryData(dataToUpload,
                    inetServerAddress, inetPageOnServer)

    elif inetPageOnServer == WEBPAGE_UL_GZIPPED_FILE:
        # BT client
        if bluetoothMode == 2:
            """
            Attempting to send the unsent media files, as well, before sending
                the current media file.
            """
            # UploadUnsentFILES()

            """
            We only use EXTENSION_ARBITRARY_FILE to specify we upload an
               arbitrary file.
            """
            return BluetoothUploadBinaryData(bluetoothServerAddress,
                    dataToUpload, EXTENSION_ARBITRARY_FILE)
        else:
            myText += "Uploading via Internet."
            DebugPrint(myText)

            if MY_DEBUG_STDERR:
                sys.stderr.write(myText + "\n")
                sys.stderr.flush()

            return InternetUploadBinaryData(dataToUpload,
                    inetServerAddress, inetPageOnServer)


"""
Uploads via Internet or Bluetooth, depending on bluetoothMode.
fileName is required for BluetoothUploadGZippedData - on Bluetooth we specify
    the type of packet sent via the file name.
The bluetoothServerAddress is global; !!!!inetServerAddress should be made
    global, then.
"""
def UploadGZippedData(aDeviceId, uncompressedData, inetServerAddress,
                                            inetPageOnServer, fileName=None):
    """
    !!!!
    struct.pack()
    UploadBinaryData(aDeviceId, uncompressedData, inetServerAddress,
        inetPageOnServer, fileName)
    """

    global MY_DEBUG_STDOUT, bluetoothMode, accessPointName, \
        bluetoothServerAddress

    #if SYMBIAN_UIQ_OS:
    DebugPrint("Entered UploadGZippedData().")

    PetWatchdog()

    myText = "UploadGZippedData(): len(uncompressedData) = %d, " \
                "inetServerAddress = %s, inetPageOnServer = %s, " \
                "fileName = %s. " % \
                (len(uncompressedData), inetServerAddress,
                    inetPageOnServer, fileName)

    if inetPageOnServer == WEBPAGE_UL_GZIPPED_STATE_AND_FILE:
        # BT client
        if bluetoothMode == 2:
            """
            Attempting to send the unsent media files, as well, before
                sending the current media file.
            """
            #UploadUnsentFILES()
            myText += "Uploading to the Bluetooth server only."
            DebugPrint(myText)

            if MY_DEBUG_STDERR_2:
                sys.stderr.write(myText + "\n")
                sys.stderr.flush()

            return BluetoothUploadGZippedData(bluetoothServerAddress,
                    uncompressedData, fileName, newMode=NEW_BT_FORMAT)
        else:
            if (accessPointName != u"") and (accessPointRetryConnect == False):
                """
                It should ALWAYS hold: aDeviceId == deviceId. Impersonating a
                    different phone is no longer necessary - I was using this
                    before, to relay a message.
                """

                """
                First condition is to avoid calling very often
                    UploadUnsentFILES() and 2nd is to check if it is the
                    BT server.
                """
                #if (not pathFileName.startswith(LOCAL_FOLDER_UNSENT_FILES))
                #   and (bluetoothMode == 1):

                # If the phone is standalone (no BT) or BT server:
                if bluetoothMode == 0 or bluetoothMode == 1:
                    """
                    Attempting to send the unsent media files, as well, before
                        sending the current media file.
                    """
                    UploadUnsentFILES()
            myText += "Uploading via Internet."

            DebugPrint(myText)

            if MY_DEBUG_STDERR:
                sys.stderr.write(myText + "\n")
                sys.stderr.flush()

            res = InternetUploadGZippedData(aDeviceId, uncompressedData,
                                        inetServerAddress, inetPageOnServer)

            DebugPrint("UploadGZippedData(%s): InternetUploadGZippedData " \
                        "returned %d." % (aDeviceId, res))
            return res

    elif inetPageOnServer == WEBPAGE_UL_GZIPPED_TEXT:
        DebugPrint(myText + \
            "Uploading text depending on bluetoothMode=%d." % bluetoothMode)

        # BT client
        if bluetoothMode == 2:
            """
            Attempting to send the unsent media files, as well, before sending
                the current media file.
            # UploadUnsentFILES()
            """
            return BluetoothUploadGZippedData(bluetoothServerAddress,
                                uncompressedData, None, newMode=NEW_BT_FORMAT)
        else:
            return InternetUploadGZippedData(aDeviceId,
                    uncompressedData, inetServerAddress,
                    inetPageOnServer)

    elif inetPageOnServer == WEBPAGE_UL_GZIPPED_FILE:
        # BT client
        if bluetoothMode == 2:
            """
            Attempting to send the unsent media files, as well, before sending
                the current media file.
            """
            # UploadUnsentFILES()
            # We only use EXTENSION_ARBITRARY_FILE to specify we upload an
            #   arbitrary file.
            return BluetoothUploadGZippedData(bluetoothServerAddress,
                    uncompressedData, EXTENSION_ARBITRARY_FILE)
        else:
            myText += "Uploading via Internet."

            DebugPrint(myText)

            if MY_DEBUG_STDERR_2:
                sys.stderr.write(myText + "\n")
                sys.stderr.flush()

            return InternetUploadGZippedData(aDeviceId,
                    uncompressedData, inetServerAddress,
                    inetPageOnServer)


def ReadGPSPosition():
    global readGPS
    global gpsInfo

    if readGPS:
        DebugPrint("Entered ReadGPSPosition() - readGPS = %s." % readGPS)

        try:
            DebugPrint("GPS last_position: %s\n" % \
                        str(positioning.last_position()) + \
                        "Reading current GPS coordinates...")

            """
            From PyS60 doc: Note that the first position()-call may take a long
                time (because of gps technology).

                position(course=0, satellites=0, callback=None,
                            interval=positioning.POSITION INTERVAL, partial=0)
            """
            gpsInfo = positioning.position(1, 1)

            DebugPrint("GPS position: %s." % gpsInfo)
        except:
            DebugPrint("ReadGPSPosition(): positioning.last_position() or " \
                        "position() returned an exception.")
            DebugPrintErrorTrace()

        appuifw.note(u"Returned from reading current GPS coordinates...",
                      "info")

        if isNaN(gpsInfo["course"]["speed"]):
            gpsInfo["course"]["speed"] = -1.0
        if isNaN(gpsInfo["course"]["heading"]):
            gpsInfo["course"]["heading"] = -1.0
        if isNaN(gpsInfo["course"]["heading_accuracy"]):
            gpsInfo["course"]["heading_accuracy"] = -1.0
        if isNaN(gpsInfo["course"]["speed_accuracy"]):
            gpsInfo["course"]["speed_accuracy"] = -1.0
    else:
        gpsInfo["position"]["latitude"] = 0.0
        gpsInfo["position"]["longitude"] = 0.0
        gpsInfo["position"]["altitude"] = 0.0
        gpsInfo["position"]["vertical_accuracy"] = 0.0
        gpsInfo["position"]["horizontal_accuracy"] = 0.0
        gpsInfo["course"]["speed"] = 0.0
        gpsInfo["course"]["heading"] = 0.0
        gpsInfo["course"]["heading_accuracy"] = 0.0
        gpsInfo["course"]["speed_accuracy"] = 0.0
        gpsInfo["satellites"]["horizontal_dop"] = 0.0
        gpsInfo["satellites"]["vertical_dop"] = 0.0
        gpsInfo["satellites"]["time_dop"] = 0.0
        gpsInfo["satellites"]["time"] = 0.0
        gpsInfo["satellites"]["used_satellites"] = -1
        gpsInfo["satellites"]["satellites"] = -1

    #DebugPrint("Exiting ReadGPSPosition()")


def CopyFile(srcPathFileName, dstFileName, myCopyBufferSize=16 * 1024):
    try:
        srcFile = open(srcPathFileName, "rb")
        dstFile = open(dstFileName, "wb")

        while True:
            myCopyBuffer = srcFile.read(myCopyBufferSize)
            if myCopyBuffer:
                dstFile.write(myCopyBuffer)
            else:
                break

        srcFile.close()
        dstFile.close()
    except:
        if MY_DEBUG_STDERR:
            sys.stderr.write("CopyFile(%s, %s, %d): exception.\n" %
                     (srcPathFileName, dstFileName,
                     myCopyBufferSize))
        DebugPrintErrorTrace()
        return -1
    return 0


def MoveFileBetweenAnyDrives(srcPathFileName, dstPathFileName):
    if ANDROID_OS or WINDOWS_CE_OS_PYTHONCE or WINDOWS_OS or RASPBIAN_OS:
        DebugPrint("Entered MoveFileBetweenAnyDrives().")

        try:
            os.rename(srcPathFileName, dstPathFileName)
        except:
            if MY_DEBUG_STDERR:
                sys.stderr.write("MoveFileBetweenAnyDrives(%s, %s): " \
                                    "exception.\n" % \
                                    (srcPathFileName, dstPathFileName))
            DebugPrintErrorTrace()
            return -1

    elif SYMBIAN_OS:
        if srcPathFileName[0:2] != dstPathFileName[0:2]:
            """
            The source and destination drives differ so moving implies copying
                src to dst and deleting src.
            """
            if CopyFile(srcPathFileName, dstPathFileName) == -1:
                return -1

            try:
                os.unlink(srcPathFileName)
            except:
                if MY_DEBUG_STDERR:
                    sys.stderr.write("MoveFileBetweenAnyDrives(%s, %s): " \
                                        "exception.\n" %
                                         (srcPathFileName, dstPathFileName))
                DebugPrintErrorTrace()
        else:
            try:
                os.rename(srcPathFileName, dstPathFileName)
            except:
                if MY_DEBUG_STDERR:
                    sys.stderr.write("MoveFileBetweenAnyDrives(%s, %s): " \
                                        "exception.\n" %
                                         (srcPathFileName, dstPathFileName))
                DebugPrintErrorTrace()

                return -1

    return 0


def RepairGoogleKeywords():
    global googleKeywords

    #!!!!TODO: look for exact minimal length required.
    #    otherwise gives "too short" exception
    MIN_LEN_GOOGLE_KEYWORDS = 15

    if len(googleKeywords) < MIN_LEN_GOOGLE_KEYWORDS:
        googleKeywords += "*" * (MIN_LEN_GOOGLE_KEYWORDS - len(googleKeywords))


def StoreLocalConfigInFile():
    global googleUsername, googleRememberPassword #, googlePassword
    global uploadMediaToYouTube, uploadMediaToPicasa, useiCamServer, \
            googleKeywords, googleMediaPrivate

    DebugPrint("Entered StoreLocalConfigInFile().")

    RepairGoogleKeywords()

    if SYMBIAN_OS:
        if SYMBIAN_3:
            """
            We call RedrawHandler() since normally it is invoked very rare on
                S^3 devices (test S^1 devices!!!) - only at the beginning of
                iCam 2-3 times and at Quit().
            """
            RedrawHandler(None)
    else:
        pass

    if gdataModulesImported == False:
        ImportGdataModules()
    """
    if (not gdataModulesImported):
        return
    """

    try:
        fOutput = open(LOCAL_CONFIG_PATH_FILENAME, "w")

        fOutput.write("# Description: Google username\n")
        fOutput.write(googleUsername + "\n")
    except:
        DebugPrintErrorTrace()

    try:
        myTextNot = "# Description: encrypted Google password - not " \
                            "stored\n\n"

        if googleRememberPassword:
            """
            Padding spaces at the end of googlePassword since it needs to be
                multiple of 16 chars long.
                From C:\Python25\Lib\site-packages\gdata\tlslite\utils\AES.py
                    assert(len(plaintext) % 16 == 0)
            """
            plainText = GetGooglePassword()

            while len(plainText) % 16 != 0:
                plainText += " "

            # cipher = Python_AES.new(AES_SECRET_KEY, 2, AES_IV)
            cipher = gdata.tlslite.utils.Python_AES.new(AES_SECRET_KEY,
                                                            2, AES_IV)

            if sys.version_info[0 : 2] == (2, 2):
                encodedText = base64.encodestring(cipher.encrypt(plainText))
            else:
                encodedText = base64.b64encode(cipher.encrypt(plainText))
            # clearText

            # At least on Python 2.2 they add an extra "\r\n" to encodedText
            encodedText = encodedText.rstrip("\r\n")
            #print "cipher.encrypt(plainText) = [%s]" % str(cipher.encrypt(plainText))
            #print "encodedText = %s" % str(encodedText)

            fOutput.write("# Description: encrypted Google password\n")
            fOutput.write(encodedText + "\n")
        else:
            fOutput.write(myTextNot)
    except:
        fOutput.write(myTextNot)
        DebugPrintErrorTrace()

    try:
        fOutput.write("# Description: uploadMediaToYouTube (0 = False, " \
                        "1 = True)\n")
        fOutput.write(str(int(uploadMediaToYouTube)) + "\n")

        fOutput.write("# Description: uploadMediaToPicasa (0 = False, " \
                        "1 = True)\n")
        fOutput.write(str(int(uploadMediaToPicasa)) + "\n")

        fOutput.write("# Description: useiCamServer (0 = No, " \
                        "1 = No media upload, 2 = All (upload State + Media, download updates and commands)\n")
        fOutput.write(str(int(useiCamServer)) + "\n")

        fOutput.write("# Description: googleRememberPassword (0 = False, " \
                        "1 = True)\n")
        fOutput.write(str(int(googleRememberPassword)) + "\n")

        fOutput.write("# Description: googleMediaPrivate (0 = False, " \
                        "1 = True)\n")
        fOutput.write(str(int(googleMediaPrivate)) + "\n")

        fOutput.write("# Description: googleKeywords (used for YouTube and " \
                        "Picasa)\n")
        fOutput.write(googleKeywords + "\n")

        fOutput.write("# Description: YOUTUBE_TEST_CLIENT_ID\n")
        fOutput.write(YOUTUBE_TEST_CLIENT_ID + "\n")

        fOutput.write("# Description: YouTube developer key\n")
        fOutput.write(youtubeDeveloperKey + "\n")

        fOutput.write("# Description: IQE_KEY\n")
        fOutput.write(IQE_KEY + "\n")

        fOutput.write("# Description: IQE_SECRET\n")
        fOutput.write(IQE_SECRET + "\n")
    except:
        DebugPrintErrorTrace()

    try:
        # fOutput.flush()
        fOutput.close()
    except:
        DebugPrintErrorTrace()


try:
    import base64
except:
    DebugPrintErrorTrace()

def GetGooglePassword():
    global googlePasswordEncrypted, googlePassword

    DebugPrint("Entered GetGooglePassword().")
    #print "Entered GetGooglePassword(): googlePassword = %s." % googlePassword

    try:
        if googlePassword is not None:
            return googlePassword

        """
        googlePasswordEncrypted contains the password read from
            LOCAL_CONFIG_PATH_FILENAME
        """
        if googlePasswordEncrypted is None:
            return ""
        else:
            if gdataModulesImported == False:
                ImportGdataModules()

            """
            I need to reinitialize the Python_AES for decryption because the
                self.IV which is initalized with the 3rd param in new() is
                changed at every Python_AES.encrypt() call, so I need self.IV
                again with the initial value.
            """
            # cipher = Python_AES.new(AES_SECRET_KEY, 2, AES_IV)
            cipher = gdata.tlslite.utils.Python_AES.new(AES_SECRET_KEY,
                                                        2, AES_IV)

            if sys.version_info[0 : 2] == (2, 2):
                googlePasswordAux = \
                    cipher.decrypt(base64.decodestring(googlePasswordEncrypted))
                #googlePasswordAux = googlePasswordAux.tostring().rstrip()
            else:
                googlePasswordAux = \
                    cipher.decrypt(base64.b64decode(googlePasswordEncrypted))
            #print "decodedText = %s" % decodedText

            """
            We remove the added spaces at the end of the password which made it
                have length % 16 == 0.
            Funny: if we leave the password with trailing spaces
                gdata + youtube don't complain and it logs in.
            """
            googlePasswordAux = googlePasswordAux.rstrip()

            if False:
            #if True:
                DebugPrint("LoadLocalConfigFromFile(): " \
                    "googlePasswordAux = %s." % googlePasswordAux)

            googlePassword = googlePasswordAux

            #return googlePasswordAux.rstrip()
            return googlePassword
    except:
        DebugPrintErrorTrace()


stateLoaded = False


def LoadLocalConfigFromFile(pathFileName):
    # global googleUsername, googlePassword, googleRememberPassword
    # global uploadMediaToYouTube, uploadMediaToPicasa, googleKeywords
    # global googleMediaPrivate
    global googleUsername, googlePasswordEncrypted, \
        googleRememberPassword
    global uploadMediaToYouTube, uploadMediaToPicasa, \
        useiCamServer, googleMediaPrivate, googleKeywords
    #global saveUnsentPackets
    #global internetUploadMaxErrors
    global YOUTUBE_TEST_CLIENT_ID, youtubeDeveloperKey
    global IQE_KEY, IQE_SECRET

    DebugPrint("Entered LoadLocalConfigFromFile()")

    """
    if not gdataModulesImported:
        return
    """
    def ReadNextNonCommentLine(fInput, rstripStr=None):
        try:
            #!!!!TODO: we can do also: for fLine in fInput:
            while True:
                fLine = fInput.readline()
                # Note that an empty line before EOF still has at least \n
                if fLine:
                    fLine = fLine.rstrip(rstripStr)
                else:
                    return None
                #if not fLine.startswith("# Description:"):
                if not fLine.startswith("#"):
                    return fLine
        except:
            DebugPrintErrorTrace()

    try:
        if os.path.isfile(pathFileName):
            fInput = open(pathFileName, "r")

            fLine = ReadNextNonCommentLine(fInput)
            if fLine is None:
                return
            googleUsername = fLine.rstrip()

            fLine = ReadNextNonCommentLine(fInput, "\r\n")
            if fLine is None:
                return
            googlePasswordEncrypted = fLine

            fLine = ReadNextNonCommentLine(fInput)
            if fLine is None:
                return
            uploadMediaToYouTube = fLine
            uploadMediaToYouTube = int(uploadMediaToYouTube)

            fLine = ReadNextNonCommentLine(fInput)
            if fLine is None:
                return
            uploadMediaToPicasa = fLine
            uploadMediaToPicasa = int(uploadMediaToPicasa)

            fLine = ReadNextNonCommentLine(fInput)
            if fLine is None:
                return
            useiCamServer = fLine
            useiCamServer = int(useiCamServer)

            fLine = ReadNextNonCommentLine(fInput)
            if fLine is None:
                return
            googleRememberPassword = fLine
            googleRememberPassword = int(googleRememberPassword)

            fLine = ReadNextNonCommentLine(fInput)
            if fLine is None:
                return
            googleMediaPrivate = fLine
            googleMediaPrivate = int(googleMediaPrivate)

            fLine = ReadNextNonCommentLine(fInput)
            if fLine is None:
                return
            googleKeywords = fLine
            RepairGoogleKeywords()

            fLine = ReadNextNonCommentLine(fInput)
            if fLine is None:
                return
            YOUTUBE_TEST_CLIENT_ID = fLine

            fLine = ReadNextNonCommentLine(fInput)
            if fLine is None:
                return
            youtubeDeveloperKey = fLine

            fLine = ReadNextNonCommentLine(fInput)
            if fLine is None:
                return
            IQE_KEY = fLine

            fLine = ReadNextNonCommentLine(fInput)
            if fLine is None:
                return
            IQE_SECRET = fLine

            # fInput.flush()
            fInput.close()
        else:
            DebugPrint("LoadLocalConfigFromFile(): could not find file %s." % \
                        pathFileName)
    except:
        DebugPrintErrorTrace()

    if MY_DEBUG_STDOUT:
        try:
            print "LoadLocalConfigFromFile(): read the following:"
            print "    googleUsername =", googleUsername
            #print "    googlePassword =", googlePassword
            print "    googleRememberPassword =", googleRememberPassword
            print "    uploadMediaToYouTube =", uploadMediaToYouTube
            print "    uploadMediaToPicasa =", uploadMediaToPicasa

            if useiCamServer == 0:
                myText = "no data"
            elif useiCamServer == 1:
                myText = "only state"
            elif useiCamServer == 2:
                myText = "all state and media data"
            print "    useiCamServer = %d (%s to iCam server)" % (useiCamServer, myText)

            print "    googleMediaPrivate =", googleMediaPrivate
            print "    googleKeywords =", googleKeywords
            print "    YOUTUBE_TEST_CLIENT_ID =", YOUTUBE_TEST_CLIENT_ID
            print "    youtubeDeveloperKey =", youtubeDeveloperKey
            print "    IQE_KEY =", IQE_KEY
            print "    IQE_SECRET =", IQE_SECRET
            sys.stdout.flush()
        except:
            DebugPrintErrorTrace()



if ANDROID_OS:
    PETTING_FILENAME = "/sdcard/iCamAlive"
elif UNIX_OS:
    PETTING_FILENAME = "./iCamAlive"
elif SYMBIAN_OS:
    PETTING_FILENAME = "D:/iCamAlive"
elif iOS_PYOBJC:
    PETTING_FILENAME = LOCAL_FOLDER + "/iCamAlive"
elif WINDOWS_OS:
    PETTING_FILENAME = LOCAL_FOLDER + "/iCamAlive"
elif WINDOWS_CE_OS_PYTHONCE:
    PETTING_FILENAME = LOCAL_FOLDER + "/iCamAlive"
elif RASPBIAN_OS:
    PETTING_FILENAME = LOCAL_FOLDER + "/iCamAlive"


def PetWatchdog():
    """
    Another idea: check modif time of STATE_PATH_FILENAME if always updating it in
        UploadStateAndFileAndStoreState() - this actually happens only for
        fileName is None.
    Maybe save file in LOCAL_FOLDER.
    """

    if ANDROID_OS:
        pass
    elif SYMBIAN_S60_OS:
    # elif SYMBIAN_OS:
        """
        On PyUIQ 0.2 it seems executing e32.ao_yield() makes Python crash with
          error - maybe because I don't have a Canvas set? !!!!:
            Program Python
            Reason code LDR-IMPORT
            Reason number 2

            and then:
            Program Py_246948292
            Reason code KERN-EXEC
            Reason number 0
        """
        e32.ao_yield()

    if MY_DEBUG_STDOUT:
        print "Entered PetWatchdog() at %s." % \
            GetCurrentDateTimeStringNice()

        if conserveEnergy == False:
            sys.stdout.flush()

    try:
        # if not os.path.exists(PETTING_FILENAME):
        if not os.path.isfile(PETTING_FILENAME):
            fOutput = open(PETTING_FILENAME, "wb")
            # fOutput.write()
            fOutput.close()
    except:
        DebugPrintErrorTrace()


try:
    import pyiqe
    hasPyIQE = True
except:
    hasPyIQE = False
    DebugPrintErrorTrace()


def IQEnginesPhotoUpload(photoPathFileName):
    if hasPyIQE == False:
        return

    try:
        # From readme.md
        iqe = pyiqe.Api(IQE_KEY, IQE_SECRET)

        # Alex commented this
        # iqe = Api(version="1.2")

        DebugPrint("IQEnginesPhotoUpload(): Time before query: %s." % \
                GetCurrentDateTimeStringWithMilliseconds())

        # "C:/OpenCV2.2/samples/python/1Good/snap00027_bla.bmp"
        (response, qid) = iqe.query(photoPathFileName)

        DebugPrint("IQEnginesPhotoUpload(): Time after query: %s. " \
                    "response = %s." % \
                    (GetCurrentDateTimeStringWithMilliseconds(),
                       str(response)))

        """
        From http://developer.iqengines.com/apidoc/current/faqs/index.html#what-is-the-crowdsourcing-api
        Can you recognize multiple objects?
        Yes! We return by default the strongest match only.
        If you would like to retrieve all objects that are identified in your
            images, you can pass the multiple_results paramter in your
            Query API request.
        """

        DebugPrint("IQEnginesPhotoUpload(): sending query with qid %s." % \
                                                                    str(qid))

        #assert response == {'data': {'error': 0}},
        #   "Invalid Response while querying: \n%s " % response
        if response != {u"data": {"error": 0}}:
            return

        DebugPrint("\nIQEnginesPhotoUpload(): waiting for results...")

        response = iqe.update()

        DebugPrint(response)

        DebugPrint("IQEnginesPhotoUpload(): Time after update(): %s." % \
                GetCurrentDateTimeStringWithMilliseconds())

        DebugPrint("\nIQEnginesPhotoUpload(): retrieving results manually.")

        # time.sleep(2)
        # time.sleep(20)
        # time.sleep(60)
        SleepAndPetWatchdog(60.0)

        response = iqe.result(qid)

        DebugPrint("IQEnginesPhotoUpload(): response = %s." % str(response))

        """
        Possible responses:
            {u'data': {u'results':
                [{u'color': u'Mostly gray brown, with some blue.',
                    u'labels': u'Titan'}],
                u'error': 0}}.
            {u'data': {u'comment':
                u"The results for qid " \
              "82e6b37dfcd16b273cd861e21c74f8c0d7a0ba6c are not available yet",
                u'error': 0}}.
        """
        if int(response[u"data"][u"error"]) != 0:
            return

        # responseToDisplay = str(response[u"data"][u"results"][0][u"labels"])
        responseToDisplay = ""
        for key in response[u"data"]:
            if key != "error":
                # print response[u"data"][key]
                responseToDisplay += str(response[u"data"][key])

        DebugPrint("IQEnginesPhotoUpload(): responseToDisplay = %s." % \
                responseToDisplay)
        #print "IQEnginesPhotoUpload(): response = %s. " \
        #   responseToDisplay = %s." % (str(response), responseToDisplay)

        DisplayNote("IQE tagged photo as %s." % responseToDisplay, -1.0)

        DebugPrint("IQEnginesPhotoUpload(): Time after result(): %s" % \
                GetCurrentDateTimeStringWithMilliseconds())
    except:
        DebugPrintErrorTrace()



youtubeClient = None
youtubeClientAlreadyConnected = False


def ConnectToYouTubeGData():
    global youtubeClient, youtubeClientAlreadyConnected

    DebugPrint("Entered ConnectToYouTubeGData().")

    try:
        youtubeClient = gdata.youtube.service.YouTubeService()
        youtubeClient.email = googleUsername
        # youtubeClient.email = "googleUser"

        youtubeClient.password = GetGooglePassword()
        # youtubeClient.password = ""

        #if True:
        if False:
            UploadByEmailAttachement(
                sender="googleUser@gmail.com",
                recipients=["alex.susu@gmail.com"],
                mediaPathFileName="/mnt/sdcard/external_sd/iCam/test.txt")

        youtubeClient.source = YOUTUBE_TEST_CLIENT_ID
        youtubeClient.developer_key = youtubeDeveloperKey
        youtubeClient.client_id = YOUTUBE_TEST_CLIENT_ID
        youtubeClient.ProgrammaticLogin()
        youtubeClientAlreadyConnected = True
    except:
        (exceptionType, exceptionValue, exceptionTraceback) = \
            sys.exc_info()
        errorStr = "Exception in ConnectToYouTubeGData() - details: " \
                    "free_ram = %d. exceptionTraceback = %s, " \
                    "exceptionType = %s, exceptionValue = %s. Bailing out..." % \
                    (GetFreeRAM(),
                       repr(traceback.format_tb(exceptionTraceback)),
                       str(exceptionType), str(exceptionValue))

        if MY_DEBUG_UPLOAD_MSG:
            UploadGZippedData(deviceId, errorStr, ICAM_SERVER_NAME,
                              WEBPAGE_UL_GZIPPED_TEXT, None)

        DebugPrint(errorStr)
        DebugPrintErrorTrace()

        if exceptionValue == "The connect operation timed out":
        # str(exceptionType).find("socket.sslerror") #<class 'socket.sslerror'>
            Quit()

        return -1


"""
def YouTubeVideoUploadThroughProxy(pathFileName, fileName, aKeyword,
        crtTime = None, mediaTimeStr = "", mediaDateStr = "", deviceId = "",
        cameraId = 0):
    DebugPrint("Entered YouTubeVideoUploadThroughProxy(pathFileName = %s, " \
                "fileName = %s, aKeyword = %s)." % (pathFileName, fileName,
                aKeyword))

    youtubeProxyServerAddress = ICAM_GAE_SERVER_NAME

    # Typical exception given: gaierror: (11001, 'getaddrinfo failed')
    #youtubeProxyServerAddress = "http://localhost:8080"
    #youtubeProxyServerAddress = "localhost:8080"

    pageOnServer = "/proxyyoutube"

    # Testing the get of proxyyoutube - it works ;)
    if False:
        try:
            httpResponseString = urllib.urlopen("http://" + \
                    youtubeProxyServerAddress + pageOnServer).read()

            DebugPrint("YouTubeVideoUploadThroughProxy(): " \
                        "httpResponseString = %s." % httpResponseString)
            return
        except:
            DebugPrintErrorTrace()

    # httpRequestHeader = {"Connection:": "Keep-alive",
    #   "Content-type": "application/x-gzip", "Accept": "text/plain"}

    #User-agent
    # httpRequestHeader = {"Content-type": "application/octet-stream",
    #   "Accept": "text/plain"}
    httpRequestHeader = {
          #"User-Agent": "Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; " \
          #     "rv:1.9.2.17) Gecko/20110420 Firefox/3.6.17",

          #"Accept": "text/plain",
          "Accept": "text/plain",

          #"Accept": "text/html,application/xhtml+xml,application/xml;" \
          #     "q=0.9,*.*;q=0.8",

          #"Accept-Language": "en-us,en;q=0.5",
          #"Accept-Encoding": "gzip,deflate",
          #"Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.7",
          "Keep-Alive": "1150",
          #"Cookie": "ACSID=AJKY",
          #"DNT": "1",
          "Connection": "keep-alive",
          #"Content-Type": "application/x-www-form-urlencoded",
          "Content-type": "application/octet-stream",
          #"Content-Length": "33"
    }

    # From http://forums.devshed.com/python-programming-11/httplib-urllib-how-do-i-keep-a-connection-open-142565.html:
    #   "there is a Connection: Keep-alive option that you can pass in the
    #   header, if you use HTTP/1.1 protocol."
    try:
        myHTTPConnection = httplib.HTTPConnection(youtubeProxyServerAddress)
        #myHTTPConnection = httplib.HTTPConnection(youtubeProxyServerAddress, 8080)

        fInput = open(pathFileName, "rb")
        dataToUpload = fInput.read()
        fInput.close()

        #dataToUpload = "content=&img=CurrentSymLink_1.3gp"

        myHTTPConnection.request("POST", pageOnServer, dataToUpload, \
                                    httpRequestHeader)

        # Get the response.
        # From http://docs.python.org/library/httplib.html:
        #   "Note that you must have read the whole response before you can
        #   send a new request to the server."
        httpResponse = myHTTPConnection.getresponse()
        httpResponseString = httpResponse.read()

        DebugPrint("YouTubeVideoUploadThroughProxy(): " \
                    httpResponseString = %s." % httpResponseString)

        #Close the connection.
        myHTTPConnection.close()
    except:
        #if MY_DEBUG_STDERR:
        #    sys.stderr.write(myText + "\n")
        DebugPrintErrorTrace()
"""

"""
btMediaTimeStr is in the format "%02d:%02d:%02d.%01d %02d-%02d-%04d" %
    (tm_hour, tm_min, tm_sec, int((numMilliseconds + 50)/ 100), tm_mday,
     tm_mon, tm_year)
btMediaDateStr is in the format "%04d-%02d-%02d" % (tm_year, tm_mon, tm_mday)
"""

class FileObjectWithLenForGdataMedia(file):
    def __init__(self, aPathFileName, accessFlags):
        # From http://www.artima.com/weblogs/viewpost.jsp?thread=236275
        #   "super returns proxy objects"
        super(FileObjectWithLenForGdataMedia,
              self).__init__(aPathFileName, accessFlags)
        self.len = os.path.getsize(aPathFileName)





"""
We use FileObjectBufferWithLenForGdataMedia to upload media files
    (video to YouTube) via a VIRTUAL file (data kept in RAM) handle.
  This is USEFUL because if we give the file directly to the GData
    YouTube API, in some cases the file handle to the file is kept
    after returning from the Insert...() call, making impossible to
    remove the file after uploading it.

GData API details:
    - Picasa:
  def InsertPhoto(self, album_or_uri, photo, filename_or_handle,
    content_type='image/jpeg'):

    - YouTube:
  def InsertVideoEntry(self, video_entry, filename_or_handle,
                       youtube_username='default',
                       content_type='video/quicktime'):
"""
class FileObjectBufferWithLenForGdataMedia(file):
    seekOffset = 0
    name = None
    dataToUpload = None

    #def __init__(self, aPathFileName, accessFlags):
    def __init__(self, aDataToUpload, *args):
        """
        if (MY_DEBUG_STDOUT):
            print "Entered FileObjectWithLenForGdataMedia::__init__(args = %s)" % str(args)
            sys.stdout.flush()
        """
        """
        #"super returns proxy objects" - from http://www.artima.com/weblogs/viewpost.jsp?thread=236275
        super(FileObjectWithLenForGdataMedia, self).__init__(aPathFileName, accessFlags)
        self.len = os.path.getsize(aPathFileName)
        """
        self.dataToUpload = aDataToUpload
        self.len = len(aDataToUpload)
        self.name = args[0]

    def close(self, *args):
        """
        DebugPrint("Am in FileObjectBufferWithLenForGdataMedia::close(%s)" % \
                                                                    str(args))
        """
        pass

    def seek(self, *args):
        DebugPrint("Entered FileObjectBufferWithLenForGdataMedia::seek(%s)" % \
                                                                    str(args))
        self.seekOffset = args[0]

    def read(self, *args):
        """
        DebugPrint("Entered " \
                "FileObjectWithLenForGdataMedia::read(args=%s)" \
                " (self.seekOffset = %d)" % \
                (str(args), self.seekOffset))
        """

        if len(args) < 1:
            res = self.dataToUpload[self.seekOffset : ]
        else:
            res = self.dataToUpload[self.seekOffset :
                                self.seekOffset + args[0]]

        lenRes = len(res)
        self.seekOffset += lenRes

        """
        DebugPrint("FileObjectWithLenForGdataMedia::read(): " \
                "returning string of len %d, and made " \
                "self.seekOffset = %d." % \
                (lenRes, self.seekOffset))
        """
        return res


def YouTubeVideoUpload(pathFileName, fileName, aKeyword, crtTime=None,
                mediaTimeStr="", mediaDateStr="", aDeviceId="", cameraId=0,
                aBatteryLevel=-1, aChargerStatus=-1):

    """
    Note:
    The fileName is the time when was given start recording (note that at least
        on Symbian there might be a small delay before actually starting to
        record).
    The (YouTube) videoDescription represents the time we entered
        UploadStateAndFileAndStoreState().
    """

    global youtubeClient, youtubeClientAlreadyConnected
    global YOUTUBE_TEST_CLIENT_ID, googleUsername, youtubeDeveloperKey

    #aKeyword += " " + btNetSearchKeywords

    DebugPrint("Entered YouTubeVideoUpload(pathFileName=%s, fileName=%s, " \
                "aKeyword=%s, aBatteryLevel=%d, aChargerStatus=%d)." % \
                (pathFileName, fileName, aKeyword, \
                    aBatteryLevel, aChargerStatus))

    if WINDOWS_CE_OS_PYTHONCE:
        DebugPrint("YouTubeVideoUpload(): WinCE doesn't have yet libssl so " \
                    "cannot perform secure connection to Google --> Bailing " \
                    "out without uploading video.")
        return -1

    if youtubeClientAlreadyConnected == False:
        if gdataModulesImported == False:
            ImportGdataModules()
        connResult = ConnectToYouTubeGData()
        """
        If connResult == -1 then don't continue (most likely bad
            username/passwd).!!!!
        """

    ytRes = 0
    fInput = None
    fInputAux = None

    try:
        """
        self.assertEquals(youtubeClient.developer_key, youtubeDeveloperKey)
        self.assertEquals(youtubeClient.client_id, YOUTUBE_TEST_CLIENT_ID)
        self.assertEquals(youtubeClient.additional_headers["X-GData-Key"],
            "key=" + youtubeDeveloperKey)
        self.assertEquals(youtubeClient.additional_headers["X-Gdata-Client"],
            YOUTUBE_TEST_CLIENT_ID)
        """

        # videoTitle = "my cool video " + str(random.randint(1000,5000))
        # videoFileName = os.path.split(pathFileName)[1]
        # videoTitle = pathFileName [: len(pathFileName) - 4]
        # videoTitle = fileName[: len(fileName) - 4]
        #videoTitle = fileName[:len(fileName) - 4] + "_" + aDeviceId
        videoTitle = fileName[:len(fileName) - 4] # Should contain deviceId and cameraId

        """
        #videoDescription = "description " + str(random.randint(1000,5000))
        videoDescription = time.strftime("%H:%M:%S %d-%m-%Y ", crtTime) + \
            aDeviceId + ", " + str(cameraId) #($mediaFileSize bytes)

        # crtTime.tm_year, crtTime.tm_mon, crtTime.tm_mday, crtTime.tm_hour
        #   crtTime.tm_min, crtTime.tm_sec #fileName
        """
        if crtTime is None:
            videoDescription = mediaTimeStr + " " + aDeviceId + ":" + \
                                    str(cameraId)  # ($mediaFileSize bytes)
        else:
            try:
                numMilliseconds = int(mediaTimeStr)
                theRest = int((numMilliseconds + 50) / 100)
            except:
                theRest = "*"
                #if MY_DEBUG_STDERR:
                #    sys.stderr.write(myText + "\n")
                DebugPrintErrorTrace()

            #videoDescription = time.strftime("%H:%M:%S.* %d-%m-%Y", crtTime) \
            #  + " " + aDeviceId + ", " + str(cameraId) #($mediaFileSize bytes)

            videoDescription = time.strftime("%H:%M:%S." + str(theRest) + \
                                                " %d-%m-%Y", crtTime) + " " + \
                                aDeviceId + ", " + str(cameraId) #($mediaFileSize bytes)

        videoDescription += " " + "%02d%%" % aBatteryLevel + \
                            " " + "%d" % aChargerStatus

        """
        import random
        developerTag01 = "tag" + str(random.randint(1000, 5000))
        developerTag02 = "tag" + str(random.randint(1000, 5000))
        developerTag03 = "tag" + str(random.randint(1000, 5000))
        """
        """
        developerTag01 = videoTitle
        developerTag02 = videoTitle
        developerTag03 = videoTitle
        """

        """
        From http://osdir.com/ml/youtube-api-gdata/2010-02/msg00091.html:
            "developer tags need to be between 3 and 25 characters"
        """
        developerTag01 = aDeviceId + " " + str(cameraId) + " " + btNetSearchKeywords
        developerTag02 = str(cameraId) + "**" #!!!!TODO use function RepairGoogleKeywords()
        developerTag03 = aDeviceId

        """
        aDuration = gdata.media.Duration()
        aDuration.seconds = "10"
        print "aDuration =", aDuration
        """

        """
        #!!!!
        # Set geographic location to 37,-122 lat, long
        where = gdata.geo.Where()
        where.set_location((37.0,-122.0))

        videoEntry = gdata.youtube.YouTubeVideoEntry(media=myMediaGroup, geo=where)
        """

        """
        See also
            https://gdata-python-client.googlecode.com/svn/trunk/pydocs/gdata.media.html#Private
        Alex: From C:\Python27\Lib\site-packages\gdata\media\__init__.py
        private = True,
            #Doesn't work - it gives AttributeError: 'bool' object has
            #    no attribute '_BecomeChildElement'
        duration = aDuration,
        'People &amp; Blogs'
        """
        myMediaGroup = gdata.media.Group(
            title=gdata.media.Title(text=videoTitle),
            description=gdata.media.Description(description_type="plain",
                                                text=videoDescription),
            keywords=gdata.media.Keywords(text=aKeyword),
            private=gdata.media.Private(),
            category=[gdata.media.Category(text="People",
                scheme="http://gdata.youtube.com/schemas/2007/categories.cat",
                label="People")],
            player=None
        )

        DebugPrint("YouTubeVideoUpload(): myMediaGroup = %s." % myMediaGroup)
        # return

        # self.assert_(isinstance(myMediaGroup, gdata.media.Group))
        videoEntry = gdata.youtube.YouTubeVideoEntry(media=myMediaGroup)
        myDeveloperTags = [developerTag01, developerTag02, developerTag03]

        devTags = videoEntry.AddDeveloperTags(myDeveloperTags)

        """
        for dev_tag in devTags:
            self.assert_(dev_tag.text in myDeveloperTags)

        self.assert_(isinstance(videoEntry, gdata.youtube.YouTubeVideoEntry))
        """

        """
        # <?xml version='1.0' encoding='UTF-8'?>
        #   <ns0:updated xmlns:ns0="http://www.w3.org/2005/Atom">
        #       2011-04-08T08:15:05.000Z
        #   </ns0:updated>

        print "videoEntry.updated =", videoEntry.updated

        # Use updated instead. It is ONLY the recorded date (without time):
        #   <?xml version='1.0' encoding='UTF-8'?>
        #      <ns0:recorded xmlns:ns0="http://gdata.youtube.com/schemas/2007">
        #           2011-04-08
        #      </ns0:recorded>
        print "videoEntry.recorded =", videoEntry.recorded

        #Somehow add
            <ns1:location xmlns:ns1="http://gdata.youtube.com/schemas/2007">
                44.419314, 26.159509 geo:alt=100
            </ns1:location>
        """


        newVideoEntry = None

        # try:
        if False:
            fInput = FileObjectWithLenForGdataMedia(pathFileName, "rb")

        fInputAux = open(pathFileName, "rb")
        dataToUpload = fInputAux.read()
        fInputAux.close()
        fInput = FileObjectBufferWithLenForGdataMedia(dataToUpload, \
                                                        pathFileName, "rb")

        """
        print "fInput =", fInput
        print "fInput.len =", fInput.len
        if isinstance(fInput, file):
            print "fInput is of type file :)"
        """
        newVideoEntry = youtubeClient.InsertVideoEntry(videoEntry, fInput)
        # Normally fInput gets closed in InsertVideoEntry().

        """
        except:
            ytRes = -1
            exceptionType, exceptionValue, exceptionTraceback = sys.exc_info()
            errorStr = "Exception in YouTubeVideoUpload() - details: " \
                        "free_ram = %d. exceptionTraceback = %s, " \
                        "exceptionType = %s, exceptionValue = %s. " \
                        "Bailing out..." % \
                        (
                            GetFreeRAM(),
                            repr(traceback.format_tb(exceptionTraceback)),
                            str(exceptionType), str(exceptionValue)
                        )
            if MY_DEBUG_UPLOAD_MSG:
                UploadGZippedData(aDeviceId, errorStr, ICAM_SERVER_NAME,
                    WEBPAGE_UL_GZIPPED_TEXT, None)

            DebugPrint(errorStr)
            DebugPrintErrorTrace()
            return ytRes
        """
        """
        # Doesn't work on Python 2.2 (e.g, PyS60 1.4.5)
        finally:
            fInput.close()
            DebugPrint("YouTubeVideoUpload(): Closed the file (attempted to) " \
                        "uploaded to YouTube.")
        """

        if ADD_VIDEO_TO_PLAYLIST:
            # ################################################################
            # Put the newly added video in the corresponding playlist (create
            #               the playlist if it doesn't exist)
            # ################################################################
            # playlistTitle = aDeviceId + ": " + \
            #   time.strftime("%Y-%m-%d", crtTime) + (", %d" % cameraId)

            if crtTime is None:
                playlistTitle = aDeviceId + ": " + mediaDateStr + ", %d" % \
                    cameraId
            else:
                playlistTitle = aDeviceId + ": " + \
                        time.strftime("%Y-%m-%d", crtTime) + ", %d" % cameraId

            playlistDescription = playlistTitle

            playlistToUse = None
            """
            !!!!We require the YouTube alias/nickname which can be different
                to the Google username!!!!!!!!
            feed = youtubeClient.GetYouTubePlaylistFeed(
                username="MultiEnder123") #NOT "ender123"?
            """
            feed = youtubeClient.GetYouTubePlaylistFeed()

            # Returns: A YouTubePlaylistFeed if successfully retrieved.
            # print "feed =", feed
            # print "feed.entry[0] =", feed.entry[0]
            for myEntry in feed.entry:
                myEntryTitle = myEntry.title.text
                # print "myEntryTitle = %s" % myEntryTitle

                #myEntry.id.text = \
                #    http://gdata.youtube.com/feeds/api/users/MultiEnder123/playlists/3FD3773F7AC5DD1E

                # myEntry.id = <xml>...
                myEntryIdStr = myEntry.id.text.split("/")[-1]
                # print "  myEntryIdStr = %s" % myEntryIdStr

                if playlistTitle == myEntryTitle:
                    playlistToUse = myEntry
                    break

            if playlistToUse is None:
                # Create the playlist if it was not found.
                # Returns: The YouTubePlaylistEntry if successfully posted.
                playlistToUse = youtubeClient.AddPlaylist(playlistTitle,
                        playlistTitle, playlist_private=False)

            # It seems this info is not used!
            aVideoTitle = ""
            # It seems this info is not used!
            aVideoDescription = ""
            playlistURI = playlistToUse.feed_link[0].href
            # time.sleep(10) #!!!!!!!!!!!!!Maybe required
            response = \
                youtubeClient.AddPlaylistVideoEntryToPlaylist(playlistURI,
                    newVideoEntry.id.text.split("/")[-1], aVideoTitle,
                    aVideoDescription)
    except:
        #newVideoEntry = youtubeClient.InsertVideoEntry(videoEntry,
        #                                                   pathFileName)

        ytRes = -1

        try:
            if fInput is not None:
                fInput.close()

            if fInputAux is not None:
                fInputAux.close()

            DebugPrint("YouTubeVideoUpload(): Closed (or attempted to) the " \
                        "file uploaded to YouTube.")
        except:
            DebugPrintErrorTrace()

        (exceptionType, exceptionValue, exceptionTraceback) = sys.exc_info()
        errorStr = \
            "Exception in YouTubeVideoUpload() - details: free_ram = %d. " \
            "exceptionTraceback = %s, exceptionType = %s, " \
            "exceptionValue = %s. Bailing out..." % \
            (GetFreeRAM(),
               repr(traceback.format_tb(exceptionTraceback)),
               str(exceptionType), str(exceptionValue))

        if MY_DEBUG_UPLOAD_MSG:
            UploadGZippedData(aDeviceId, errorStr, ICAM_SERVER_NAME,
                              WEBPAGE_UL_GZIPPED_TEXT, None)

        DebugPrint(errorStr)

        DebugPrintErrorTrace()

        """
        Since we had an error when uploading to YouTube, then save the media
            file to the Unsent folder.
        # storeLocallyMedia == 0 because in case we don't store it THEN we
        #   save it in Unsent.
        """
        if (storeLocallyMedia == 0) and (useiCamServer != 2):
            if saveUnsentPackets > 0:
                """
                Note: we could save this file in LOCAL_FOLDER_MEDIA, but in
                    LOCAL_FOLDER_UNSENT_FILES is more appropriate.
                """
                MoveFileBetweenAnyDrives(pathFileName,
                        LOCAL_FOLDER_UNSENT_FILES + "/" + fileName)

        TreatException("YouTubeVideoUpload()")

    return ytRes


picasaClient = None
picasaClientAlreadyConnected = False


def ConnectToPicasaGData(aDeviceId=deviceId):
    global picasaClient, picasaClientAlreadyConnected

    DebugPrint("Entered ConnectToPicasaGData().")

    try:
        # Initialize the client
        picasaClient = gdata.photos.service.PhotosService()
        """
        picasaClient.email = picasaUsername
        picasaClient.password = picasaPassword
        """
        picasaClient.email = googleUsername
        picasaClient.password = GetGooglePassword()
        picasaClient.source = "iCam Client"
        picasaClient.ProgrammaticLogin()
        picasaClientAlreadyConnected = True
    except:
        (exceptionType, exceptionValue, exceptionTraceback) = sys.exc_info()

        errorStr = "Exception in ConnectToPicasaGData() - details: " \
                    "free_ram = %d. exceptionTraceback = %s, " \
                    "exceptionType = %s, exceptionValue = %s. " \
                    "Bailing out..." % \
                    (GetFreeRAM(),
                       repr(traceback.format_tb(exceptionTraceback)),
                       str(exceptionType), str(exceptionValue))

        if MY_DEBUG_UPLOAD_MSG:
            UploadGZippedData(aDeviceId, errorStr, ICAM_SERVER_NAME,
                              WEBPAGE_UL_GZIPPED_TEXT, None)

        DebugPrint(errorStr)
        DebugPrintErrorTrace()

        """
        Example of exception that I am "hunting":
            Exception in ConnectToPicasaGData() - details: free_ram = 15552512.
            exceptionTraceback = [
            ' File "a.py", line 2132, in ConnectToPicasaGData',
            ' File "C:\\Python25\\lib\\site-packages\\gdata\\service.py", line 771, in ProgrammaticLogin',
            ' File "C:\\Python25\\lib\\site-packages\\atom\\http.py", line 163, in request',
            ' File "newcore\\Lib\\httplib.py", line 860, in endheaders',
            ' File "newcore\\Lib\\httplib.py", line 732, in _send_output',
            ' File "newcore\\Lib\\httplib.py", line 699, in send',
            ' File "newcore\\Lib\\httplib.py", line 1135, in connect',
            ' File "newcore\\Lib\\socket.py", line 79, in ssl'],
            exceptionType = , exceptionValue = The connect operation timed out. Bailing out...
        """
        if exceptionValue == "The connect operation timed out":
            Quit()

        return -1


#!!!!TODO: add in Picasa description aBatteryLevel, aChargerStatus.
def PicasaPhotoUpload(pathFileName, fileName=None, aKeyword="", crtTime=None,
                mediaTimeStr="", mediaDateStr="", aDeviceId="", cameraId=0,
                aBatteryLevel=-1, aChargerStatus=-1, aData=None):
    global picasaClient, picasaClientAlreadyConnected

    DebugPrint("Entered PicasaPhotoUpload(pathFileName=%s, fileName=%s, " \
                "aKeyword=%s, aBatteryLevel=%d, aChargerStatus=%d)." % \
                (pathFileName, fileName, aKeyword, \
                    aBatteryLevel, aChargerStatus))

    DebugPrint("    PicasaPhotoUpload(): mediaTimeStr=%s, mediaDateStr=%s, " \
                "aDeviceId=%s, cameraId=%s." % \
                (str(mediaTimeStr), str(mediaDateStr), str(aDeviceId), \
                    str(cameraId)))

    #DebugPrint("    PicasaPhotoUpload(): aData=%s ." % str(aData))

    fInput = None

    if picasaClientAlreadyConnected == False:
        if gdataModulesImported == False:
            ImportGdataModules()
        ConnectToPicasaGData()

    try:
        # Give the album a unique title by appending the current time.

        #test_album = picasaClient.InsertAlbum('Python library test' +
        #   str(GetTime()), 'A temporary test album.')

        #albumFeed = picasaClient.GetUserFeed(kind = "album")
        #albumFeed = picasaClient.GetUserFeed(kind = ["album", "photo"])

        #albumFeed = picasaClient.GetUserFeed(kind = ["album", "photo",
        #   "comment", "tag"], user = "ender123")

        # These don't work probably (just in YouTube's client case) because
        #   the gdata server doesn't answer requests to
        #   http(s)://picasaweb.google.com.

        #albumFeed = picasaClient.GetEntry(
        #  "http://picasaweb.google.com/data/feed/api/user/" + "ender123") #None

        #albumFeed = picasaClient.GetEntry(
        #  "https://picasaweb.google.com/data/feed/api/user/" + "ender123") #None

        # print "albumFeed =", albumFeed

        # albumTitle = time.strftime("%Y-%m-%d", time.localtime(crtTime))
        # albumTitle = time.strftime("%Y-%m-%d", crtTime)
        if crtTime is None:
            albumTitle = aDeviceId + ": " + mediaDateStr + ", %d" % cameraId
        else:
            albumTitle = aDeviceId + ": " + time.strftime("%Y-%m-%d",
                                                crtTime) + ", %d" % cameraId

        DebugPrint("    PicasaPhotoUpload(): albumTitle=%s ." % str(albumTitle))

        iCamAlbumFound = False

        """
        From https://code.google.com/apis/picasaweb/docs/1.0/developers_guide_python.html#ListAlbums
            (less important:
             https://code.google.com/apis/picasaweb/docs/2.0/developers_guide_protocol.html#ListAlbums)
        """
        # albumsFeed = picasaClient.GetUserFeed(kind = "album", user = "ender123")
        albumsFeed = picasaClient.GetUserFeed(kind="album")

        """
        # IMPORTANT NOTE: it does not print the album list - I believe
        #    BECAUSE THE tostring() METHOD DOESN'T DUMP THE ALBUMS LIST :))
        DebugPrint("albumsFeed = %s" % str(albumsFeed))
        """
        album = None
        for album in albumsFeed.entry:
            # print "album =", album
            DebugPrint("Title: %s, number of photos: %s, id: %s" % \
                        (album.title.text, album.numphotos.text,
                        album.gphoto_id.text))
            # picasaClient.Delete(album)

            if album.title.text == albumTitle:
                iCamAlbumFound = True
                DebugPrint("    PicasaPhotoUpload(): found albumTitle!")
                break

        if iCamAlbumFound:
            # print "Found"
            iCamAlbum = album
        else:
            # print "Not found"
            """
            !!!!IMPORTANT: Unfortunately this private argument gets translated
                in Picasa in the attribute Visibility - "Anyone with the link".
            Currently to fix this, the user has to go on Picasa and edit the
                attribute manually.
            Find a programatic solution...!!!!
            """
            iCamAlbum = picasaClient.InsertAlbum(title=albumTitle,
                    summary="iCam photo album.", location="Bucharest", #!!!!TODO: make location configurable
                    access="private")

        photoEntry = gdata.photos.PhotoEntry()

        photoEntry.title = atom.Title(text=fileName)

        photoEntry.private = gdata.media.Private()

        #photoEntry.summary = atom.Summary(text=fileName)
        photoSummary = "%02d%%" % int(GetBatteryLevelPercentage()) + \
                        " " + "%s" % str(GetChargerStatus())
        photoEntry.summary = atom.Summary(text=photoSummary)

        photoEntry.category.append(atom.Category(
                        scheme="http://schemas.google.com/g/2005#kind",
                        term="http://schemas.google.com/photos/2007#photo"))

        photoFileNameExtension = str.lower(pathFileName[len(pathFileName)-4 :])

        if photoFileNameExtension == ".jpg":
            aContentType = "image/jpeg"
        elif photoFileNameExtension == ".png":
            aContentType = "image/png"

        #if fileName != None:
        if aData == None:
            entry = picasaClient.InsertPhoto(iCamAlbum, photoEntry,
                        pathFileName, content_type=aContentType)
        else:
            #if fileName == None:
            #assert aData != None

            fInput = FileObjectBufferWithLenForGdataMedia(aData, \
                                                        pathFileName, "rb")
            entry = picasaClient.InsertPhoto(iCamAlbum, photoEntry,
                                fInput, content_type=aContentType)
            # TODO: check!!!! Normally  fInput gets closed in InsertPhoto()

        """
        'video/mp4' is from
            https://code.google.com/apis/picasaweb/docs/2.0/developers_guide_protocol.html#PostVideo
            (see maybe also
            https://code.google.com/apis/picasaweb/docs/2.0/reference.html).
        But although the documentation above says we can upload video, the
            latest gdata API doesn't allow uploading videos on Picasa:

            Traceback (most recent call last):
              File "iCam.py", line 84, in testInsertPhotoUpdateBlobAndDelete
                pathFileName, content_type='video/mp4')
              File "C:\Python25\lib\site-packages\gdata\photos\service.py", line 396, in InsertPhoto
                ['image/'+t for t in SUPPORTED_UPLOAD_TYPES]

        GooglePhotosException: (602, "Accepted content types: ['image/bmp',
            'image/jpeg', 'image/jpg', 'image/gif', 'image/png']",
            'This is not a valid content type: video/mp4')
        """
    except:
        try:
            if fInput is not None:
                fInput.close()

            DebugPrint("PicasaPhotoUpload(): Closed (or attempted to) the " \
                        "file uploaded to Picasa.")
        except:
            DebugPrintErrorTrace()

        #entry = picasaClient.InsertVideo(iCamAlbum, photoEntry, pathFileName,
        #    content_type='video/mp4')

        # """

        #self.assert_(entry.id.text)
        #updated_entry = picasaClient.UpdatePhotoBlob(entry, pathFileName)

        #self.assert_(entry.GetEditLink().href !=
        #    updated_entry.GetEditLink().href)

        # picasaClient.Delete(updated_entry)

        # In case there was an error when uploading to YouTube, then save the
        #   media file to the Unsent folder.
        """
        storeLocallyMedia == 0 because in case we don't store it THEN we
           save it in Unsent
        """
        if (storeLocallyMedia == 0) and (useiCamServer != 2):
            if saveUnsentPackets > 0:
                """
                Note: we could save this file in LOCAL_FOLDER_MEDIA, but in
                    Unsent is more appropriate.
                """
                MoveFileBetweenAnyDrives(pathFileName,
                        LOCAL_FOLDER_UNSENT_FILES + "/" + fileName)

        (exceptionType, exceptionValue, exceptionTraceback) = sys.exc_info()
        errorStr = "Exception in PicasaPhotoUpload() - details: free_ram = %d."\
                    " exceptionTraceback = %s, exceptionType = %s, " \
                    "exceptionValue = %s. Bailing out..." % \
                    (GetFreeRAM(),
                       repr(traceback.format_tb(exceptionTraceback)),
                       str(exceptionType), str(exceptionValue))

        if MY_DEBUG_UPLOAD_MSG:
            UploadGZippedData(aDeviceId, errorStr, ICAM_SERVER_NAME,
                              WEBPAGE_UL_GZIPPED_TEXT, None)

        DebugPrint(errorStr)

        DebugPrintErrorTrace()

        if exceptionValue == (403, "Forbidden", "Token expired"):
            Quit()

        return -1


def GetYouTubeUserProfile():
    global googleUsername

    userEntry = youtubeClient.GetYouTubeUserEntry(
                    "https://gdata.youtube.com/feeds/users/" +
                    googleUsername)

    DebugPrint("GetYouTubeUserProfile(): userEntry.description.text = %s" % \
                            str(userEntry.description.text))
    #print errorStr

    return userEntry.description.text


"""
It is really mandatory to have both parameters fileName and pathFileName
    because the callers can pass pathFileName with values
    "D:/iCamTemp.3gp" (or pathFileName = "D:/iCamTemp.jpg", or
    "D:/iCamTemp.amr")
  and they should pass the useful names (e.g., 2011_01_10_13_50_00_100.3gp)
    through fileName.
If (fileName is None) we only save state.bin.
We use (fileName == "[!NO_FILE]") to send only the state to the server
    - e.g., used when wanting to send GPS info, but no media.
"""
if SYMBIAN_OS:
    if _PyS60_1_9_OR_NEWER: #pyS60VersionNumber > 14:
        mediaUploadedLock = e32.Ao_lock()




STORE_STATE = True
"""
stateMarshalled is None when StoreState() is NOT called from
    UploadStateAndFileAndStoreState()
  stateMarshalled - we can specify the state, s.t. we don't call
    BuildState() anymore.
"""
def StoreState(aDeviceId=deviceId, stateMarshalled=None):
    global STORE_STATE

    if not STORE_STATE:
        return

    if SYMBIAN_S60_OS:
        if SYMBIAN_3:
            # !!Maybe do it even less often
            if stateMarshalled is None:
                """
                We call RedrawHandler() since normally it is invoked very
                    rare on S^3 devices (test S^1 devices!!!) - only at
                    the beginning of iCam 2-3 times and at Quit().
                """
                RedrawHandler(None)
    elif ANDROID_OS:
        """
        After a few calls this might crash the Option Menu
            (maybe the SL4A "server", as well) - and I'm stuck
        """
        DisplayRedrawInfo(partialForAndroid=True)

    if stateMarshalled is None:
    #if aDeviceId is None:
        #aDeviceId = deviceId
        aCrtTime = GetCurrentDateTime()
        stateMarshalled = BuildState(cameraId=-1, crtTime=aCrtTime,
                                numMilliseconds=0, fileName=None,
                                pathFileName=None)

    try:
        # if os.path.exists(STATE_PATH_FILENAME_BACKUP):
        if os.path.isfile(STATE_PATH_FILENAME_BACKUP):
            os.unlink(STATE_PATH_FILENAME_BACKUP)
    except:
        DebugPrintErrorTrace()

    try:
        # if os.path.exists(STATE_PATH_FILENAME):
        if os.path.isfile(STATE_PATH_FILENAME):
            os.rename(STATE_PATH_FILENAME, STATE_PATH_FILENAME_BACKUP)

        DebugPrint("StoreState(): renamed %s to %s." % \
                            (STATE_PATH_FILENAME, STATE_PATH_FILENAME_BACKUP))
    except:
        DebugPrintErrorTrace()

    try:
        fOutput = open(STATE_PATH_FILENAME, "wb")
        # fOutput.write(stateMarshalled)
        fOutput.write(stateMarshalled.encode("zlib"))
        # fOutput.flush()
        fOutput.close()

        DebugPrint("StoreState(): Wrote state file %s." % \
                                                        STATE_PATH_FILENAME)
    except:
        (exceptionType, exceptionValue, exceptionTraceback) = sys.exc_info()
        errorStr = "Exception in StoreState() " \
                    "when writing locally stateMarshalled to %s - " \
                    "details: free_ram = %d. exceptionTraceback = %s, "\
                    "exceptionType = %s, exceptionValue = %s. " \
                    "Bailing out..." % \
                    (STATE_PATH_FILENAME, GetFreeRAM(),
                       repr(traceback.format_tb(exceptionTraceback)),
                       str(exceptionType), str(exceptionValue))

        if MY_DEBUG_UPLOAD_MSG:
            UploadGZippedData(aDeviceId, errorStr,
                    ICAM_SERVER_NAME, WEBPAGE_UL_GZIPPED_TEXT, None)

        DebugPrint(errorStr)

        DebugPrintErrorTrace()

        return -1

    return 0


"""
We declare it here, just before the function UploadStateAndFileAndStoreState()
    which uses it, because we use it also in the 2 functions
    MyFuncYouTubeVideoUpload() and MyFuncYouTubeVideoUpload(), inner to
        UploadStateAndFileAndStoreState().
"""
googleRes = None
NUM_RETRIES_SEND_VIA_YOUTUBE = 2 #3

def UploadStateAndFileAndStoreState(aDeviceId, cameraId,
                    fileName, pathFileName,
                    inetServerAddress, pageOnServer, singleThreaded=False):

    global googleRes

    DebugPrint("Entered UploadStateAndFileAndStoreState() at %s." % \
                            GetCurrentDateTimeStringWithMilliseconds())

    try:
        crtTime = GetCurrentDateTime()
        crtTime2 = GetTime()
        # See http://discussion.forum.nokia.com/forum/showthread.php?116978-What-is-the-time-granularity-in-Pys60 .
        numMilliseconds = (crtTime2 - int(crtTime2)) * 1000

        # PetWatchdog()

        """
        In case we upload media file to Google, BUT NOT to iCam server 
            (useiCamServer == 1, uploadMediaToYouTube = True,
                uploadMediaToPicasa = True)
            we still report in the state data the file size and the filename.
          But we just don't send the media file - the server can easily
            realize this.
        """
        #if (useiCamServer == 1) or (fileName == NO_MEDIA_FILE_NAME):
        if fileName == NO_MEDIA_FILE_NAME:
            fileName2 = NO_MEDIA_FILE_NAME
        else:
            fileName2 = fileName
        stateMarshalled = BuildState(cameraId, crtTime, numMilliseconds,
                                                    fileName2, pathFileName)

        DebugPrint("UploadStateAndFileAndStoreState(): " \
                        "len(stateMarshalled) = %d." % len(stateMarshalled))

        """
        stateStr = "%d %d %d %d %d %d " % (GetBatteryLevelPercentage(),
                            pauseInterval, burstModeIsStarted,
                            photoResolutionIndex, digitalZoom, photoQuality)
        """

        # We can save the state at each invocation.
        #res = StoreState(aDeviceId, stateMarshalled)

        if fileName is None:
            res = StoreState(aDeviceId, stateMarshalled)
            return res

        googleRes = 0

        #######################################################################
        #######################################################################
        ########### First, we upload the media to YouTube/Picasa. #############
        #######################################################################
        #######################################################################
        if str.lower(fileName[len(fileName) - 4:]) in [".3gp", ".mp4"]:
            # if serversUsed == 4:
            if uploadMediaToYouTube:

                def MyFuncYouTubeVideoUpload():
                    global googleRes
                    """
                    googleRes = YouTubeVideoUpload(pathFileName, fileName,
                                    googleKeywords, crtTime, None, None,
                                    deviceId, cameraId)
                    """
                    for i in range(NUM_RETRIES_SEND_VIA_YOUTUBE):
                        googleRes = YouTubeVideoUpload(pathFileName, fileName,
                            googleKeywords, crtTime, str(int(numMilliseconds)),
                            None, deviceId, cameraId,
                            int(GetBatteryLevelPercentage()),
                            int(GetChargerStatus()))

                        if googleRes == -1:
                            DebugPrint("MyFuncYouTubeVideoUpload(): " \
                                        "YouTubeVideoUpload() returned %d." %
                                        googleRes)
                        else:
                            break

                        # Do an "exponential backoff" time interval wait
                        SleepAndPetWatchdog( int(0.1 * (2**i)) )
                    else:
                        """
                        Executed only if break was not used --> we have reached
                            this only if we failed NUM_RETRIES_SEND_VIA_YOUTUBE
                            times.
                        """
                        DebugPrint("We failed NUM_RETRIES_SEND_VIA_YOUTUBE " \
                                    "times --> we drop this media packet.")

                    """
                    When using FileObjectBufferWithLenForGdataMedia() we do
                        not really require to use mediaUploadedLock.
                      In principle, the other thread, which can go back to
                        VideoRecordAndUpload() can proceed without waiting to
                        finish the upload, although this is not great since
                        YouTube puts a limit on the frequency of videos
                        uploaded on the established connection.
                    """
                    if singleThreaded == False:
                        if SYMBIAN_OS:
                            if _PyS60_1_9_OR_NEWER: #pyS60VersionNumber > 14:
                                mediaUploadedLock.signal()

                if singleThreaded == True:
                    MyFuncYouTubeVideoUpload()
                else:
                    # if storeLocallyMedia == 0:
                    #    MyFuncYouTubeVideoUpload()
                    MyThreadStart(MyFuncYouTubeVideoUpload)

        elif str.lower(fileName[len(fileName) - 4:]) in \
                                                    [".jpg", ".png", ".bmp"]:
            # mediaFileData = ""
            # if serversUsed == 4:
            if uploadMediaToPicasa:

                def MyFuncPicasaPhotoUpload():
                    global googleRes
                    # PicasaPhotoUpload(albumTitle, pathFileName, fileName)
                    # PicasaPhotoUpload(pathFileName, fileName, crtTime)
                    """
                    googleRes = PicasaPhotoUpload(pathFileName, fileName,
                                    googleKeywords, crtTime, None, None,
                                    deviceId, cameraId)
                    """
                    googleRes = PicasaPhotoUpload(pathFileName, fileName,
                            googleKeywords, crtTime, str(int(numMilliseconds)),
                            None, deviceId, cameraId)

                    if singleThreaded == False:
                        if SYMBIAN_OS:
                            if _PyS60_1_9_OR_NEWER: #pyS60VersionNumber > 14:
                                mediaUploadedLock.signal()

                if singleThreaded == True:
                    MyFuncPicasaPhotoUpload()
                else:
                    # if storeLocallyMedia == 0:
                    #    MyFuncPicasaPhotoUpload()
                    MyThreadStart(MyFuncPicasaPhotoUpload)
            # mediaFileData = ""

        if googleRes == -1:
            # if SYMBIAN_OS:
            if SYMBIAN_S60_OS:
                # audio.say(u"I can't get no satisfaction")
                audio.say(u"Error")
            else:
                pass


        #######################################################################
        #######################################################################
        #######We uploaded (if requested) the media to YouTube/Picasa.#########
        ######## Now we prepare to upload media to the iCam server. ###########
        #######################################################################
        #######################################################################
        mediaFileData = ""
        # if serversUsed != 4:

        #if uploadMediaToiCamServer:
        if useiCamServer == 2:
            if fileName == NO_MEDIA_FILE_NAME:
                """
                We do not append to the SMF packet the media data:
                    mediaFileData == ""
                """
                pass
            else:
                """
                if uploadMediaToYouTube and \
                            (str.lower(fileName[-4:]) in [".3gp", ".mp4"]):
                    pass
                """

                # Read the binary file from disk.
                try:
                    fInput = open(pathFileName, "rb")
                    mediaFileData = fInput.read()
                    fInput.close()
                except:
                    (exceptionType, exceptionValue, exceptionTraceback) = \
                                                            sys.exc_info()
                    errorStr = "Exception in " \
                                "UploadStateAndFileAndStoreState() at %s " \
                                "with pathFileName = %s - details: " \
                                "free_ram = %d. " \
                                "exceptionTraceback = %s, " \
                                "exceptionType = %s, " \
                                "exceptionValue = %s. Bailing out..." \
                                % (
                                    GetCurrentDateTimeStringNice(),
                                    pathFileName,
                                    GetFreeRAM(),
                                    repr(traceback.format_tb(exceptionTraceback)),
                                    str(exceptionType),
                                    str(exceptionValue)
                                )

                    if MY_DEBUG_UPLOAD_MSG:
                        UploadGZippedData(aDeviceId, errorStr,
                                ICAM_SERVER_NAME,
                                WEBPAGE_UL_GZIPPED_TEXT, None)

                    DebugPrint("Exception in " \
                            "UploadStateAndFileAndStoreState() at %s " \
                            "with pathFileName = %s. Bailing out..." % \
                            (GetCurrentDateTimeStringNice(),
                               pathFileName))

                    DebugPrintErrorTrace()

                    return -1

                if ERASE_ORIGINAL_MEDIA_FILE_AFTER_READ:
                    try:
                        os.unlink(pathFileName)
                    except:
                        # os.remove()
                        DebugPrintErrorTrace()

        """
        UploadText("The JPEG has %d bytes." % len(data), ICAM_SERVER_NAME, \
                    WEBPAGE_UL_GZIPPED_TEXT)
        """

        #data = message + STRING_SEPARATOR + filename + STRING_SEPARATOR + data

        uploadData = stateMarshalled + mediaFileData

        """
        UploadText("The uncompressed SMF has %d bytes." % len(data), \
                    ICAM_SERVER_NAME, WEBPAGE_UL_GZIPPED_TEXT)
        """

        DebugPrint("UploadStateAndFileAndStoreState(): Sending data of " \
                    "size %d (accessPointName = %s, bluetoothMode = %d)." % \
                    (len(uploadData), accessPointName, bluetoothMode))
        #return UploadGZippedData(data, ......, inetServerAddress,
        #                                           pageOnServer)

        # if inetServerAddress is None:
        myText = "UploadStateAndFileAndStoreState() - details: " \
                    "aDeviceId = %s, fileName = %s, pathFileName = %s, " \
                    "cameraId = %d, time = %s, free_ram = %d." % \
                    (
                        aDeviceId, fileName, pathFileName,
                        cameraId, GetCurrentDateTimeStringNice(),
                        GetFreeRAM()
                    )

        DebugPrint(myText)

        if MY_DEBUG_STDERR_2:
            sys.stderr.write(myText + "\n")
            sys.stderr.flush()

        # Normally, if no error, UploadGZippedData returns 0
        res = 0

        #if USE_ICAM_SERVER or bluetoothMode == 2:
        if (useiCamServer > 0) or (bluetoothMode == 2):
            res = UploadGZippedData(aDeviceId, uploadData, inetServerAddress,
                                    pageOnServer, fileName)

        if res == -1:
            # if SYMBIAN_OS:
            if SYMBIAN_S60_OS:
                # audio.say(u"I can't get no satisfaction")
                audio.say(u"Error")

        return res
    except:
        DebugPrintErrorTrace()


"""
Used by BluetoothMessageProcessAndDelete(btMsgId) --> WE MUST NOT CHECK FOR
    uploadUnsentData HERE, BUT IN THE caller of this function.
"""
def UploadUnsentBinaryData(aDeviceId, fileName, fileData):
    global deviceId

    try:
        PetWatchdog()

        myText = "UploadUnsentBinaryData(aDeviceId = %s, fileName = %s): at " \
                    "%s, calling InternetUploadBinaryData() with data of " \
                    "size %d." % (aDeviceId, fileName,
                                 GetCurrentDateTimeStringNice(), len(fileData))

        DebugPrint(myText)

        if MY_DEBUG_UPLOAD_MSG:
            UploadGZippedData(deviceId, myText, ICAM_SERVER_NAME,
                                  WEBPAGE_UL_GZIPPED_TEXT, None)

        """
        if NEW_BT_FORMAT:
            if fileName[-4:] in BT_OBEX_EXTENSION_LIST_TXT:
                serverPageToConnectTo = WEBPAGE_UL_GZIPPED_TEXT
            elif fileName[-4:] in BT_OBEX_EXTENSION_LIST_SMF:
                serverPageToConnectTo = WEBPAGE_UL_GZIPPED_STATE_AND_FILE
            elif fileName[-4:] in BT_OBEX_EXTENSION_LIST_FIL:
                serverPageToConnectTo = WEBPAGE_UL_GZIPPED_FILE
        else:
            if fileName.endswith(EXTENSION_TEXT_MESSAGE):
                serverPageToConnectTo = WEBPAGE_UL_GZIPPED_TEXT
            elif fileName.endswith(EXTENSION_STATE_AND_MEDIA_FILE):
                serverPageToConnectTo = WEBPAGE_UL_GZIPPED_STATE_AND_FILE
            elif fileName.endswith(EXTENSION_ARBITRARY_FILE):
                serverPageToConnectTo = WEBPAGE_UL_GZIPPED_FILE
        """
        if fileName.endswith(EXTENSION_TEXT_MESSAGE):
            serverPageToConnectTo = WEBPAGE_UL_GZIPPED_TEXT
        elif fileName.endswith(EXTENSION_STATE_AND_MEDIA_FILE):
            serverPageToConnectTo = WEBPAGE_UL_GZIPPED_STATE_AND_FILE
        elif fileName.endswith(EXTENSION_ARBITRARY_FILE):
            serverPageToConnectTo = WEBPAGE_UL_GZIPPED_FILE

        res = InternetUploadBinaryData(fileData, ICAM_SERVER_NAME,
                                                serverPageToConnectTo)

        """
        UploadStateAndFileAndStoreState(aDeviceId, cameraId, fileName, pathFileName,
                            aYear, aMonth, aDay, aHour, aMinute, aSecond,
                            ICAM_SERVER_NAME, WEBPAGE_UL_GZIPPED_STATE_AND_FILE)
        """
        if res == -1:
            DebugPrint("UploadUnsentBinaryData(%s): unsuccessful " \
                        "InternetUploadBinaryData(). Returning..." % aDeviceId)
            """
            !!!!!!!!I should save the unsent BT message for later resend(s).
            fOutput = open(pathFileName, "rb")
            fileData = fInput.read()
            fInput.close()
            """
            return

        """
        if aDeviceId == deviceId:
            DebugPrint("UploadUnsentBinaryData(%s): After successful " \
                        "InternetUploadBinaryData(), deleting temporary " \
                        "unsent file %s." % (aDeviceId, pathFileName))
            os.unlink(pathFileName)
        else:
            DebugPrint("UploadUnsentBinaryData(%s) - relayed data: " \
                        "After successful InternetUploadBinaryData(), " \
                        "deleting unsent file %s - remember that we have " \
                        "the corresponding media file already saved in the " \
                        "Media folder." % (aDeviceId, pathFileName))
            os.unlink(pathFileName)
        """
    except:
        #import shutil
        #shutil.move(pathFileName, LOCAL_FOLDER_MEDIA_FILES + "/" + fileName)
        DebugPrintErrorTrace()


def UploadUnsentFile(aDeviceId, pathFileName):
    global deviceId

    try:
        PetWatchdog()

        myText = "UploadUnsentFile(%s): at %s, calling " \
                    "InternetUploadBinaryData() with data from file %s of " \
                    "size %d bytes." % \
                    (aDeviceId, GetCurrentDateTimeStringWithMilliseconds(),
                       pathFileName, os.path.getsize(pathFileName))

        DebugPrint(myText)

        if MY_DEBUG_UPLOAD_MSG:
            UploadGZippedData(deviceId, myText, ICAM_SERVER_NAME,
                                    WEBPAGE_UL_GZIPPED_TEXT, None)

        """
        #!!!!
        if pathFileName.endswith(EXTENSION_COMMAND_MESSAGE):
            serverPageToConnectTo = WEBPAGE_UL_GZIPPED_TEXT
        """

        if pathFileName.endswith(EXTENSION_TEXT_MESSAGE):
            serverPageToConnectTo = WEBPAGE_UL_GZIPPED_TEXT
        elif pathFileName.endswith(EXTENSION_STATE_AND_MEDIA_FILE):
            serverPageToConnectTo = WEBPAGE_UL_GZIPPED_STATE_AND_FILE
        elif pathFileName.endswith(EXTENSION_ARBITRARY_FILE):
            serverPageToConnectTo = WEBPAGE_UL_GZIPPED_FILE

        fInput = open(pathFileName, "rb")
        fileData = fInput.read()
        fInput.close()

        """
        #To experiment burst of packets you can do a forever loop and send one
        #    unsent packet
        while True:
        """
        res = InternetUploadBinaryData(fileData, ICAM_SERVER_NAME,
                                            serverPageToConnectTo)
        """
        UploadStateAndFileAndStoreState(aDeviceId, cameraId,
            fileName, pathFileName,
            aYear, aMonth, aDay, aHour, aMinute, aSecond,
            ICAM_SERVER_NAME, WEBPAGE_UL_GZIPPED_STATE_AND_FILE)
        """

        if res == -1:
            """
            DebugPrint("UploadUnsentFile(%s): unsuccessful " \
                        "InternetUploadBinaryData(). Returning..." % \
                        (aDeviceId))
            return
            """
            DebugPrint("UploadUnsentFile(%s): unsuccessful " \
                        "InternetUploadBinaryData(). " \
                        "InternetUploadBinaryData() saved to transmit later " \
                        "the data." % aDeviceId)

        if aDeviceId == deviceId:
            DebugPrint("UploadUnsentFile(%s): After successful " \
                        "InternetUploadBinaryData(), deleting temporary " \
                        "media file %s." % \
                        (aDeviceId, pathFileName))
            os.unlink(pathFileName)
        else:
            DebugPrint("UploadUnsentFile(%s) - relayed data: After " \
                        "successful InternetUploadBinaryData(), deleting " \
                        ".sfm file %s - remember that we have the " \
                        "corresponding media file already saved in the " \
                        "Media folder." % (aDeviceId, pathFileName))
            os.unlink(pathFileName)

            """
            newPathName = LOCAL_FOLDER_MEDIA_FILES + "/" + aDeviceId
            newPathFileName = newPathName + "/" + fileName
            DebugPrint("UploadUnsentFile(%s): After successful " \
                        "InternetUploadBinaryData(), moving file %s to %s - " \
                        "after unmarshalling the media file from it." % \
                        (aDeviceId, pathFileName, newPathFileName))

            if not os.path.exists(newPathName):
                os.makedirs(newPathName)

            os.rename(pathFileName, newPathFileName)
            """
    except:
        #import shutil
        #shutil.move(pathFileName, LOCAL_FOLDER_MEDIA_FILES + "/" + fileName)
        DebugPrintErrorTrace()


def DoNotUploadUnsent():
    global deviceId, conserveEnergy, uploadUnsentData

    return (uploadUnsentData != 1 and uploadUnsentData != 3) or \
            conserveEnergy or (deviceId == IMEI_N95)


def UploadUnsentFolder(aDeviceId, pathFolderName):
    try:
        DebugPrint("UploadUnsentFolder(%s): pathFolderName = %s." % \
                                            (aDeviceId, pathFolderName))

        folderContent = os.listdir(pathFolderName)

        """
        # Use reverse = False to send first the oldest ones (like this you
        #   send in chronological order). Use reverse = True for sending
        #   first the most recent ones.
        sortedFolderContent = sorted(folderContent, reverse = False)
        """
        # sort() without parameters is the ONLY one that works in Python 2.2.
        #   (Info on sort at http://wiki.python.org/moin/HowTo/Sorting/.)
        folderContent.sort()
        sortedFolderContent = folderContent

        counterUnsentPackets = 0
        for fileName in sortedFolderContent:
            #if True:
            if fileName.endswith(EXTENSION_STATE_AND_MEDIA_FILE) or \
                                fileName.endswith(EXTENSION_ARBITRARY_FILE):
                # or if (fileName.endswith(EXTENSION_TEXT_MESSAGE):
                if DoNotUploadUnsent():
                    DebugPrint("UploadUnsentFolder(): exiting " \
                                "UploadUnsentFolder() because " \
                                "conserveEnergy = %d or " \
                                "uploadUnsentData = %d or deviceId = %s." % \
                                (conserveEnergy, uploadUnsentData,
                                   deviceId))
                    return

                pathFileName = pathFolderName + "/" + fileName

                """
                This should happen only when processing the Unsent folder
                    itself.
                """
                if os.path.isdir(pathFileName):
                    pass
                else:
                    UploadUnsentFile(aDeviceId, pathFileName)

                if counterUnsentPackets % \
                            NUM_UNSENT_PACKETS_BEFORE_DOWNLOAD_COMMANDS == \
                            NUM_UNSENT_PACKETS_BEFORE_DOWNLOAD_COMMANDS - 1:
                    hasDownloadedNewCmd = DownloadCommands()

                counterUnsentPackets += 1
    except:
        DebugPrintErrorTrace()


def UploadUnsentFILES():
    global deviceId, conserveEnergy, uploadUnsentData

    if NoInternetConnection():
        DebugPrint("UploadUnsentFILES(): NOT entering UploadUnsentFILES() " \
                    "because accessPointName = %s and " \
                    "accessPointRetryConnect = %d." % \
                    (accessPointName, accessPointRetryConnect))
        return

    if DoNotUploadUnsent():
        DebugPrint("UploadUnsentFILES(): NOT entering UploadUnsentFILES() " \
                    "because conserveEnergy = %d or " \
                    "uploadUnsentData = %d or deviceId = %s." % \
                    (conserveEnergy, uploadUnsentData, deviceId))
        return
    else:
        DebugPrint("UploadUnsentFILES(): Entering UploadUnsentFILES().")
        """
        Attempting to send the unsent media files, as well, before sending the
            current media file.
        #UploadUnsentFILES()
        """

    UploadUnsentFolder(deviceId, LOCAL_FOLDER_UNSENT_FILES)

    # Process also the subfolders of Unsent, which are for the BT clients.
    try:
        folderContent = os.listdir(LOCAL_FOLDER_UNSENT_FILES)

        for folderName in folderContent:
            # if os.path.isdir(folderContent[i]):
            folderPathName = LOCAL_FOLDER_UNSENT_FILES + "/" + folderName

            DebugPrint("UploadUnsentFILES(): folderPathName = %s." % \
                                                        folderPathName)

            if os.path.isdir(folderPathName):
                # folderContent[i] is the aDeviceId
                UploadUnsentFolder(folderName, folderPathName)
    except:
        DebugPrintErrorTrace()



try:
    import smtplib

    from email.MIMEMultipart import MIMEMultipart
    from email.MIMEBase import MIMEBase
    from email.MIMEText import MIMEText
    from email.Utils import COMMASPACE, formatdate
    from email import Encoders
except:
    DebugPrint("Cannot import modules smtplib and email.*")
    DebugPrintErrorTrace()

def UploadByEmailAttachement(sender, recipients, mediaPathFileName):
    # Inspired from http://docs.python.org/2/library/email-examples.html
    global googleUsername, googlePassword

    DebugPrint("Entered UploadByEmailAttachement()")

    if False:
        DebugPrint("    UploadByEmailAttachement(): googleUsername=%s, " \
                "googlePassword = %s" % (googleUsername, googlePassword))

    try:
        # From http://stackoverflow.com/questions/3362600/how-to-send-email-attachments-with-python/3362673#3362673
        send_to = recipients

        msg = MIMEMultipart()
        msg["From"] = sender
        #msg["To"] = COMMASPACE.join(send_to)
        msg["To"] = ", ".join(send_to)
        msg["Date"] = formatdate(localtime=True)
        #msg["Subject"] = subject
        msg["Subject"] = "Our family reunion2 from Android"

        text = "This is a test email with attachement"

        msg.attach( MIMEText(text) )

        part = MIMEBase("application", "octet-stream")
        part.set_payload( open(mediaPathFileName, "rb").read() )
        Encoders.encode_base64(part)
        part.add_header(
            "Content-Disposition",
            'attachment; filename="%s"' % os.path.basename(mediaPathFileName))
        msg.attach(part)

        #print "msg.as_string() =", msg.as_string()

        server = smtplib.SMTP("smtp.gmail.com:587")

        # The server.ehlo() are very important for GMail - otherwise, it doesn't work
        server.ehlo()
        server.starttls()
        server.ehlo() # From http://stackoverflow.com/questions/12030179/issue-sending-email-with-python
        server.login(googleUsername, googlePassword)

        server.sendmail(sender, recipients, msg.as_string())

        #server.quit()

        server.close()

        #print "Successfully sent the mail to smtp.gmail.com"
    except:
        DebugPrintErrorTrace()


try:
    import ftplib
except:
    DebugPrint("Cannot import module ftplib.")
    DebugPrintErrorTrace()

def UploadByFTP(mediaPathFileName):
    # Inspired from [Beazley_2009], Chapter 22

    ftpURL = ""
    ftpUsername = ""
    ftpPassword = ""

    ftpConnection = ftplib.FTP(ftpURL, ftpUsername, ftpPassword)
    fInput = open(mediaPathFileName, "rb")
    # Send file to the FTP server
    res = ftpConnection.storbinary("STOR " + mediaPathFileName, fInput)
    # Close the connection
    ftpConnection.close()


#UploadByFTP(mediaPathFileName="./vlc-help_std.txt")


def StoreConfig_CamAutoCfg_WinCE():
    DebugPrint("Entered StoreConfig_CamAutoCfg_WinCE().")

    try:
        fOutput = open(LOCAL_FOLDER + "/CamAuto.cfg", "wb")
        fOutput.write("%d %d" % (videoRecordDuration[0],
                      pauseInterval))
        fOutput.close()
    except:
        DebugPrintErrorTrace()


###############################################################################
###############################################################################
###############################################################################
###############################################################################
##############################Bluetooth########################################
###############################################################################
###############################################################################
###############################################################################
###############################################################################
bluetoothModeList = [u"Bluetooth not used",
                     u"Bluetooth server and Internet proxy mode",
                     u"Bluetooth client mode"]  # u"No Bluetooth",
                                                # basestation
# This var is required as global.
bluetoothFormSaved = False

bluetoothInbox = None


# Uses a Form
def SelectBluetoothMode():
    global bluetoothMode, bluetoothServerAddress
    global bluetoothFormSaved, useiCamServer
    global uploadMediaToYouTube, uploadMediaToPicasa

    """
    Note that myFields contains a combo with values
        frombluetoothModeList, from which we choose the initial value
        the one of index bluetoothMode.
    """
    myFields = [(u"Bluetooth mode for iCam Phone", "combo",
                (bluetoothModeList, bluetoothMode))]

    if ANDROID_OS:
        """
        DisplayNote("Bluetooth functionality not YET implemented for iCam " \
                    "for Android!")
        """
        try:
            bluetoothMode = DialogMultipleChoices(myFields[0][0], \
                                bluetoothModeList[0:2], int(bluetoothMode))
        except:
            DebugPrintErrorTrace()
    elif SYMBIAN_OS:
        """
        From http://wiki.forum.nokia.com/index.php/How_to_use_Form_in_Python_for_S60
            (and also http://www.mobilenin.com/pys60/info_tabs_forms.htm):
        """
        if bluetoothMode == -1:
            """
            bluetoothMode is used as default value index in combo in myFields
                and it has to be >= 0 and < len(bluetoothModeList)
            """
            bluetoothMode = 0

        try:
            # Initialize a boolean variable to know whether the form is saved
            bluetoothFormSaved = False


            appuifw.app.title = u"Bluetooth Intranet"

            # Creates the form
            #bluetoothForm = appuifw.Form(myFields,
            #   flags=appuifw.FFormEditModeOnly)
            bluetoothForm = appuifw.Form(myFields, \
                    flags=appuifw.FFormEditModeOnly | appuifw.FFormDoubleSpaced)

            """
            popupMenuList = []
            for i in range(len(bluetoothModeList)):
                if i == bluetoothMode:
                    popupMenuList += [unicode(MENU_SELECT_PREFIX
                                      + bluetoothModeList[i])]
                else:
                    popupMenuList += [unicode(bluetoothModeList[i])]

            resMenu = appuifw.popup_menu(popupMenuList,
                                         u"Select phone Bluetooth mode for iCam")
            """


            # bluetoothForm = appuifw.Form(myFields, appuifw.FFormEditModeOnly)

            # Define a function to be called when the form is saved
            def BluetoothFormSaved(arg):
                global bluetoothFormSaved
                bluetoothFormSaved = True
                return True

            # Assign the save function
            bluetoothForm.save_hook = BluetoothFormSaved

            # Show the form. This operation is blocking until we close the form.
            bluetoothForm.execute()

            # After the form is saved and closed, display the information
            if bluetoothFormSaved == True:
                # print bluetoothForm[0][2]
                """
                The combo form field value is a long integer. We convert it to
                    int because we would receive "TypeError: Form combo field,
                    bad index" at the next instantiation of appuifw.Form().
                """
                bluetoothMode = int(bluetoothForm[0][2][1])
                # StoreState()

                if bluetoothMode == 0:
                    bluetoothServerAddress = "no_BT"
                    StoreState()
                elif bluetoothMode == 1:
                    bluetoothServerAddress = "BTServer"
                    StoreState()
                    # Spawn a "thread":
                    # global bluetoothServerTimer
                    # bluetoothServerTimer.after(1, BluetoothServer)
                    DebugPrint("SelectBluetoothMode(): Calling " \
                                "BluetoothServerInitialize().")

                    """
                    from threading import Thread
                    bluetoothServerThread = Thread(target = BluetoothServer)
                    bluetoothServerThread.start()
                    #time.sleep(1)
                    """
                    """
                    BluetoothServerInitialize()
                    time.sleep(2)
                    """
                    # """

                    """
                    Does not work on PyS60 1.4.5 - see Weekly snippets
                        Aug 1st, 2010
                    """
                    # thread.start_new_thread(BluetoothServer, ())
                    BluetoothServerInitialize()

                    """
                    This might take a long time to complete, if there are
                       many BT messages in Inbox.
                    """
                    BluetoothMessageListProcess(processJustNonSMF_BtMsgs=True)
                elif bluetoothMode == 2:
                    # This is BT client mode.

                    # time.sleep(1)
                    # e32.ao_sleep(1)
                    # """

                    """
                    To send the media file in the BT message we need to have
                        useiCamServer = 2. Otherwise, we send only the state.
                    """
                    uploadMediaToYouTube = 0
                    uploadMediaToPicasa = 0
                    useiCamServer = 2

                    """
                    We want to ask (in next BluetoothClientDiscoverServer()
                        call) for the server in SelectBluetoothMode().
                    """
                    bluetoothServerAddress = ""
                    BluetoothClientDiscoverServer()
                    BluetoothClientInitializeInbox()
                    StoreState()
                    BluetoothMessageListProcess(processJustNonSMF_BtMsgs=True)
        except:
            DebugPrintErrorTrace()
        """
        finally: # Doesn't work in Python 2.2 (e.g., PyS60 1.4.5)
            appuifw.app.title = ICAM_APP_TITLE
        """
        appuifw.app.title = ICAM_APP_TITLE



bluetoothUploadErrorsCounter = 0
NUM_RETRIES_SEND_VIA_BT = 3

def BluetoothUploadBinaryData(bluetoothRecipientAddress, aData, fileName):
    global bluetoothServerOPPServicePort
    global deviceId
    global bluetoothUploadErrorsCounter

    """
    DebugPrint("Entered BluetoothUploadBinaryData(" \
            "bluetoothRecipientAddress = %s, fileName = %s, " \
            "len(aData) = %d, aData = %s)." % \
            (bluetoothRecipientAddress, fileName, len(aData), aData))
    """
    DebugPrint("Entered BluetoothUploadBinaryData(" \
                "bluetoothRecipientAddress = %s, fileName = %s, " \
                "len(aData) = %d)." % \
                (bluetoothRecipientAddress, fileName, len(aData)))

    try:
        """
        fileName is the name of the file to be sent via BT:
            - COMMANDS_FILENAME
            - with extension .fil
            - media file with extension: ".jpg", ".png", ".3gp", ".mp4".
        if fileName == None --> message is TXT BT message.
        """
        if fileName != None:

            # CMD type
            if fileName == COMMANDS_FILENAME:
                if NEW_BT_FORMAT:
                    btFileName = BT_OBEX_FILENAME_PREFIX + \
                        GetCurrentDateTimeStringWithMilliseconds() + \
                        "_" + deviceId + "_x" + \
                        BT_OBEX_EXTENSION_CMD #EXTENSION_COMMAND_MESSAGE

                    tmpPathFileName = LOCAL_FOLDER + "/" + btFileName

                    #unsentFileName = btFileName + "." + COMMANDS_FILENAME
                    unsentFileName = btFileName
                else:
                    # time.strftime("%Y_%m_%d_%H_%M_%S", GetCurrentDateTime())
                    btFileName = BT_OBEX_FILENAME_PREFIX + \
                          BT_OBEX_FILENAME_PREFIX_TYPE_CMD + \
                          GetCurrentDateTimeStringWithMilliseconds()

                    unsentFileName = btFileName + "." + COMMANDS_FILENAME

                    tmpPathFileName = LOCAL_FOLDER + "/" + btFileName

            # FIL type
            elif str.lower(fileName).endswith(EXTENSION_ARBITRARY_FILE):
            # elif fileName.endswith(EXTENSION_ARBITRARY_FILE):
                if NEW_BT_FORMAT:
                    btFileName = BT_OBEX_FILENAME_PREFIX + \
                        GetCurrentDateTimeStringWithMilliseconds() + \
                        "_" + deviceId + "_x" + \
                        EXTENSION_ARBITRARY_FILE
                else:
                    btFileName = BT_OBEX_FILENAME_PREFIX + \
                        BT_OBEX_FILENAME_PREFIX_TYPE_FIL + \
                        GetCurrentDateTimeStringWithMilliseconds()

                unsentFileName = btFileName + EXTENSION_ARBITRARY_FILE

                tmpPathFileName = LOCAL_FOLDER_MEDIA_FILES + "/" + btFileName

            # SMF type
            #elif str.lower(fileName[-4:]) in [".jpg", ".png", ".3gp", ".mp4"]:
            elif str.lower(fileName[-4:]) in BT_OBEX_EXTENSION_LIST_SMF:
                # iCam_jpg_2010_10_31_...
                # tmpPathFileName will look like iCam_SMF_jpg_2010_10_31_...

                """
                We assume this is a SMF file (the filename can be .jpg, .3gp,
                    .mp4, etc). We should check this NOT ASSUME IT!!!!
                """
                #tmpPathFileName = LOCAL_FOLDER_MEDIA_FILES + "/" + \
                #   BT_OBEX_FILENAME_PREFIX + fileName
                LEN_EXT_FILENAME = 3

                """
                tmpPathFileName = LOCAL_FOLDER + "/" + \
                    BT_OBEX_FILENAME_PREFIX + fileName[len(fileName) -
                    LEN_EXT_FILENAME : LEN_EXT_FILENAME] + "+" +
                    fileName[0 : len(fileName) - (LEN_EXT_FILENAME + 1)]
                """

                if NEW_BT_FORMAT:
                    # fileName[len(fileName) - LEN_EXT_FILENAME :]
                    #btFileName = fileName[:-4] + "_bt" + fileName[-4:]
                    btFileName = BT_OBEX_FILENAME_PREFIX + \
                        fileName[:len(fileName) - 4] + \
                        fileName[len(fileName) - 4:]
                else:
                    """
                    # time.strftime("%Y_%m_%d_%H_%M_%S", GetCurrentDateTime())
                    btFileName = BT_OBEX_FILENAME_PREFIX + \
                        BT_OBEX_FILENAME_PREFIX_TYPE_TXT + \
                        GetCurrentDateTimeStringWithMilliseconds()

                    # time.strftime("%Y_%m_%d_%H_%M_%S", GetCurrentDateTime())
                    btFileName = BT_OBEX_FILENAME_PREFIX + \
                        BT_OBEX_FILENAME_PREFIX_TYPE_SMF + \
                        fileName[len(fileName) - 3 : ] + \
                        GetCurrentDateTimeStringWithMilliseconds()
                    """
                    btFileName = BT_OBEX_FILENAME_PREFIX + \
                                BT_OBEX_FILENAME_PREFIX_TYPE_SMF + \
                                fileName[len(fileName) - LEN_EXT_FILENAME:] + \
                                "_" + fileName[0: len(fileName) - \
                                (LEN_EXT_FILENAME + 1)]

                # btFileName + EXTENSION_STATE_AND_MEDIA_FILE
                unsentFileName = btFileName
                # unsentFileName = btFileName + EXTENSION_STATE_AND_MEDIA_FILE

                tmpPathFileName = LOCAL_FOLDER_MEDIA_FILES + "/" + \
                                        btFileName

                DebugPrint("BluetoothUploadBinaryData(): btFileName = %s." % \
                                                        btFileName)

                """
                We want to have the media file at the beginning of aData and
                    the state at the end, but UploadStateAndFileAndStoreState()
                    puts them in reversed order.
                """
                if NEW_BT_FORMAT:
                    if fileName == COMMANDS_FILENAME:
                        pass
                    else:
                        myOffset = struct.calcsize(statePackFormat)
                        aData = aData[myOffset:] + aData[0 : myOffset]

            # Unknown type
            else:
                DebugPrint("BluetoothUploadBinaryData(): fileName has " \
                            "unknown extension.")

        else: 
            """
            TEXT message (else for "if fileName != None:" above)
                Note that here we create the BT message name.
            """
            if NEW_BT_FORMAT:
                btFileName = BT_OBEX_FILENAME_PREFIX + \
                    GetCurrentDateTimeStringWithMilliseconds() + \
                    "_" + deviceId + "_x" + \
                    BT_OBEX_EXTENSION_TXT #EXTENSION_TEXT_MESSAGE

                unsentFileName = btFileName
            else:
                btFileName = BT_OBEX_FILENAME_PREFIX + \
                    BT_OBEX_FILENAME_PREFIX_TYPE_TXT + \
                    GetCurrentDateTimeStringWithMilliseconds()
                    # time.strftime("%Y_%m_%d_%H_%M_%S", GetCurrentDateTime())

                unsentFileName = btFileName + EXTENSION_TEXT_MESSAGE

            """
            tmpPathFileName = LOCAL_FOLDER + "/" + BT_OBEX_FILENAME_PREFIX + \
                        time.strftime(BT_OBEX_FILENAME_PREFIX_TYPE_TXT +
                        "%Y_%m_%d_%H_%M_%S", GetCurrentDateTime())
            """
            tmpPathFileName = LOCAL_FOLDER_MEDIA_FILES + "/" + btFileName

        if WINDOWS_OS:
            """
            This is just for testing purposes - we do not send via
                BT, but put the file in the virtual "recipient" BT inbox folder
            """
            #tmpPathFileName = LOCAL_FOLDER_MEDIA_FILES + "/" + \
            tmpPathFileName = BLUETOOTH_INBOX_PATH + "/" + btFileName

        DebugPrint("BluetoothUploadBinaryData(): tmpPathFileName = %s, " \
                    "unsentFileName = %s" % (tmpPathFileName, unsentFileName))

        if NEW_BT_FORMAT:
            aDataBT = aData
        else:
            """
            We add header word with the length of the BT msg (except this word).
                Note that this length dword is added at the
                beginning of the Bluetooth transmission packet, but is not
                added for the Internet transmission.
            """

            """
            IMPORTANT: We require < to specify little endian and ALSO no
                alignment (see http://docs.python.org/library/struct.html)
            """
            aDataBT = struct.pack("<i", len(aData)) + aData

        """
        The name of the file sent is like sLog_2010_10_29_12_56_17_0.jpg or
            sLog_2010_10_29_12_53_33_1.3gp, although the file is not a .jpg or
            .3gp because it contains extra data at the beginning and the
            resulting data is gzipped.
        """
        fOutput = open(tmpPathFileName, "wb")
        fOutput.write(aDataBT)
        fOutput.close()
    except:
        DebugPrintErrorTrace()

        return -1


    # If we make it global func, then it should be passed unsentFileName, aData
    def SaveFileToUnsent():
        DebugPrint("Entered SaveFileToUnsent().")

        if saveUnsentPackets == 0:
            return
        try:
            fOutput = open(LOCAL_FOLDER_UNSENT_FILES + "/" + unsentFileName,
                            "wb")
            fOutput.write(aData)
            fOutput.close()
        except:
            DebugPrintErrorTrace()

    if bluetoothRecipientAddress not in bluetoothServerOPPServicePort:
        # Check again if the server is on.
        BluetoothClientDiscoverServer()

        if bluetoothRecipientAddress not in bluetoothServerOPPServicePort:
            DebugPrint("BluetoothUploadBinaryData(): bluetooth server does " \
                        "not run OPP (OBEX Push Profile) service. Bailing " \
                        "out after saving file to Unsent.")

            """
            # Requires backslashes, otherwise btsocket.bt_obex_send_file gives
            #   exception: error: (22, 'Invalid argument')
            tmpPathFileNameWithBackslashes = tmpPathFileName.replace("/", "\\")
            MoveFileBetweenAnyDrives(tmpPathFileNameWithBackslashes,
                LOCAL_FOLDER_UNSENT_FILES + "/" + fileName)
            """
            # Erase the file that failed to be sent via BT.
            if os.path.isfile(tmpPathFileName):
                """
                This is not necessary if MoveFileBetweenAnyDrives() is
                    successful.
                """
                try:
                    if deviceId not in [IMEI_E7, IMEI_WinOS]:
                    #if deviceId != IMEI_E7:
                        os.unlink(tmpPathFileName)
                except:
                    DebugPrintErrorTrace()
                    #sys.stdout.flush()

            SaveFileToUnsent()
            return -1

    DebugPrint("BluetoothUploadBinaryData(): Calling " \
            "bt_obex_send_file(bluetoothRecipientAddress = %s, " \
            "bluetoothServerOPPServicePort[...] = %s, tmpPathFileName = %s)." % \
            (bluetoothRecipientAddress,
                str(bluetoothServerOPPServicePort[bluetoothRecipientAddress]),
                tmpPathFileName))

    resFunc = 0

    if SYMBIAN_OS:
        tmpPathFileNameWithBackslashes = tmpPathFileName.replace("/", "\\")

        for i in range(NUM_RETRIES_SEND_VIA_BT):
            try:
                """
                Requires backslashes, otherwise btsocket.bt_obex_send_file
                    gives exception: error: (22, 'Invalid argument')
                """
                btsocket.bt_obex_send_file(bluetoothRecipientAddress,
                    bluetoothServerOPPServicePort[bluetoothRecipientAddress],
                    unicode(tmpPathFileNameWithBackslashes))

                break
            except:
                DebugPrint("BluetoothUploadBinaryData(): Received exception at " \
                        "btsocket.bt_obex_send_file().")
                DebugPrintErrorTrace()
                bluetoothUploadErrorsCounter += 1

            # Do an "exponential backoff" time interval wait
            SleepAndPetWatchdog( int(0.1 * (2**i)) )
        else:
            """
            Executed only if break was not used --> we have reached this only
                if we failed NUM_RETRIES_SEND_VIA_BT times.
            """
            DebugPrint("We failed NUM_RETRIES_SEND_VIA_BT times --> we drop " \
                        "this packet and save it to unsent.")
            resFunc = -1
            SaveFileToUnsent()

        """
        MoveFileBetweenAnyDrives(tmpPathFileNameWithBackslashes,
            LOCAL_FOLDER_UNSENT_FILES + "/" + fileName)
        """

        """
        !!!!TODO: Do this only for error:
                (12, 'Not enough space'), then restart phone.
            Apparently?? on Nokia 6680, when this error is received the only
            way to recover is to restart phone (or application!!!!).
        """
        if deviceId == IMEI_6680:
            if bluetoothUploadErrorsCounter >= 15:
                BluetoothClientDiscoverServer()
                BluetoothClientInitializeInbox()

                if len(bluetoothServerOPPServicePort.items()) > 0:
                #if bluetoothServerOPPServicePort != -1:
                    RestartPhone()

    #elif ANDROID_OS: #!!!!TODO

    # Erase the file sent via BT.
    if os.path.isfile(tmpPathFileName):
        # This is not necessary if MoveFileBetweenAnyDrives() was used.
        try:
            if deviceId not in [IMEI_E7, IMEI_WinOS]:
                os.unlink(tmpPathFileName)
        except:
            DebugPrintErrorTrace()
            #sys.stdout.flush()

    """
    if MY_DEBUG_STDOUT:
        appuifw.note(u"BluetoothUploadBinaryData(): sent %d bytes." % \
            len(aDataBT), "info")
    """
    DebugPrint("BluetoothUploadBinaryData(): sent to %s %d bytes; " \
                "resFunc = %d." % \
                (bluetoothRecipientAddress, len(aDataBT), resFunc))

    return resFunc



# bluetoothClientTimer = e32.Ao_timer()

"""
If fileName is None, then aData is a text message (.txm).
Otherwise, it is a state and media file (.smf).
"""
def BluetoothUploadGZippedData(bluetoothRecipientAddress, aData,
                                fileName, newMode=False):
    global bluetoothClientTimer
    global address  #, services
    global deviceId

    DebugPrint("BluetoothUploadGZippedData(bluetoothRecipientAddress=%s, " \
                        "aData, fileName=%s, newMode=%d)" % \
                        (bluetoothRecipientAddress, \
                                fileName, newMode))

    try:
        """
        DebugPrint("Entered BluetoothUploadGZippedData(" \
                    "bluetoothRecipientAddress = %s, fileName = %s)." % \
                    (bluetoothRecipientAddress, fileName))
        """

        if newMode:
            # For CMD messages we do NOT add the header
            if fileName == COMMANDS_FILENAME:
                aData = AddPacketHeader(aData)
                #pass

            # For TEXT and FIL messages we add the header
            elif (fileName is None) or \
                                fileName.endswith(EXTENSION_ARBITRARY_FILE):
                aData = AddPacketHeader(aData)

            # For BT SMF messages we add footer
            else:
                #aData = aData + struct.pack(deviceIdFormat, deviceId)
                aData = AddPacketHeader(aData, footer=True)
        else:
            # """
            # add deviceId identifier in front of aData
            # aData = struct.pack("100s", deviceId) + aData
            aData = AddPacketHeader(aData)

            """
            We gzip the data to reduce amount of data uploaded - usually
                good for energy reduction.

            !!!!We should actually specify in the iCam packet if the file is
                    encoded or not!!!!
            """
            if newMode == False:
                aData = aData.encode("zlib")

        return BluetoothUploadBinaryData(bluetoothRecipientAddress,
                                        aData, fileName)
    except:
        DebugPrintErrorTrace()
        return -1

    # print "The address is", address
    # print "The OBEX port value is", services[BT_RFCOMM_SERVICE_NAME]
    # sys.stdout.flush()

    #addressPair = (address, services[BT_RFCOMM_SERVICE_NAME])

    # Reconnecting to the server, etc.
    #bluetoothClientTimer.after(1, BluetoothClient)
    #BluetoothClientDiscoverServer()
    #bluetoothClientTimer.after(1, BluetoothUploadGZippedDataWrapper(aData))
    return 0


"""
BluetoothUploadGZippedDataWrapper = lambda myData: (lambda :
                                            BluetoothUploadGZippedData(myData))
"""

"""
btMsgId is a string for ANDROID_OS, and an integer for SYMBIAN_OS.
"""
counterErrorsDeleteBluetoothMessage = 0


BLUETOOTH_INBOX_PATH = None
if ANDROID_OS:
    BLUETOOTH_INBOX_PATH = "/mnt/sdcard/bluetooth"
    if not os.path.exists(BLUETOOTH_INBOX_PATH):
        BLUETOOTH_INBOX_PATH = "/bluetooth" #!!!!TODO: think better
elif SYMBIAN_OS:
    if SYMBIAN_3:
        BLUETOOTH_INBOX_PATH = "E:/Received files"
        # This is case for E7. What about others: e.g., N8 - can it be on C:?
    else:
        BLUETOOTH_INBOX_PATH = None
elif RASPBIAN_OS:
    BLUETOOTH_INBOX_PATH = "/home/pi/bluetooth_files/"
#PATH_DELIMITER = "/"
#PATH_DELIMITER = "\\"

"""
putInErrorFolderIfPossible works only where BT messages are DIRECTLY
    accessible via the FS (Symbian 3+, Android, NOT Symbian 2nd ed).
"""
def BluetoothDeleteMessage(btMsgId, putInErrorFolderIfPossible=False):
    global counterErrorsDeleteBluetoothMessage, bluetoothInbox

    if ANDROID_OS or SYMBIAN_3 or WINDOWS_OS or RASPBIAN_OS:
        DebugPrint("Entered BluetoothDeleteMessage(btMsgId=%s)." % str(btMsgId))
        #return

        try:
            if putInErrorFolderIfPossible:
                btInboxSentToiCamPath = BLUETOOTH_INBOX_PATH + "/BTMessagesError"
            else:
                #btInboxSentToiCamPath = BLUETOOTH_INBOX_PATH + "/BTMessagesSent"
                btInboxSentToiCamPath = BLUETOOTH_INBOX_PATH + "/BTMessagesDone" #"/BTMessagesDeleted"

            if not os.path.exists(btInboxSentToiCamPath):
                os.makedirs(btInboxSentToiCamPath)

            srcPathFileName = BLUETOOTH_INBOX_PATH + "/" + btMsgId
            destPathFileName = btInboxSentToiCamPath + "/" + btMsgId

            if False: #True:
                MoveFileBetweenAnyDrives(srcPathFileName, destPathFileName)
            else:
                os.unlink(srcPathFileName)
        except:
            DebugPrintErrorTrace()

    elif SYMBIAN_OS:
        DebugPrint("Entered BluetoothDeleteMessage(btMsgId=%d)." % btMsgId)

        try:
            """
            From Logs\Nokia6680\2011_07_07_2\stderr_2011_07_07_11_46_01.txt:
                "SymbianError: [Errno -14] KErrInUse"
            """
            bluetoothInbox.delete(btMsgId)
        except:
            counterErrorsDeleteBluetoothMessage += 1

            if counterErrorsDeleteBluetoothMessage == 3:
                DebugPrint("BluetoothDeleteMessage(): " \
                    "counterErrorsDeleteBluetoothMessage = %d --> " \
                    "restarting phone." % \
                    counterErrorsDeleteBluetoothMessage)

                RestartPhone()
            """
            !!!!If we receive
                "SymbianError: [Errno -14] KErrInUse"
                (see http://mobile-revival.110mb.com/ReVival/N95N95N95N95N95/FromPhone/stderr_2011_06_13_10_02_41.txt)
                try to Quit() or restart phone.
            """
            DebugPrintErrorTrace()


def GetBluetoothMessageName(btMsgId):
    if ANDROID_OS or RASPBIAN_OS:
        #pathFileNameBluetoothMessage = BLUETOOTH_INBOX_PATH + "/" + btMsgId
        res = str(btMsgId)
    elif SYMBIAN_OS:
        if SYMBIAN_3:
            #pathFileNameBluetoothMessage = BLUETOOTH_INBOX_PATH + "/" + btMsgId
            res = str(btMsgId)
        else: #(S60_EDITION[0] < 3):
            #pathFileNameBluetoothMessage = BLUETOOTH_INBOX_PATH + "/" + btMsgId
            """
            description(btMsgId) returns the name of the file from the BT
                message.
            """
            res = str(bluetoothInbox.description(btMsgId))
    elif WINDOWS_OS:
        res = str(btMsgId)

    return res


def GetBluetoothMessagePathFileName(btMsgId):
    if ANDROID_OS:
        res = BLUETOOTH_INBOX_PATH + "/" + btMsgId
        #res = btMsgId
    elif RASPBIAN_OS:
        res = BLUETOOTH_INBOX_PATH + "/" + btMsgId
    elif WINDOWS_OS:
        res = BLUETOOTH_INBOX_PATH + "/" + btMsgId
    elif SYMBIAN_OS:
        if SYMBIAN_3:
            res = BLUETOOTH_INBOX_PATH + "/" + btMsgId
            #res = btMsgId
        elif S60_EDITION[0] >= 3:
            # S60 3rd-5th ed
            """
            This includes (AFAIR) SYMBIAN_1 phones.

            The following command gives exception KErrNotSupported on 
                S60 3+ edition (Symbian v9) - can't access the file
                although it exists (at least when storing messages on
                Mem card...) :(.

            #Will not get the path on S60 3+ edition (Symbian v9).
            pathFileNameBluetoothMessage = bluetoothInbox.attachment_path(btMsgId)
            """
            #!!!! Here we don't have filename so we return None #the int btMsgId
            res = None #btMsgId
        elif S60_EDITION[0] < 3:
            # For S60 2nd edition.
            """
            This should work on S60 2nd edition. However, it will not get the
                path on S60 3+ edition (Symbian v9) - it will generate an exception
                which is normal behavior, so we choose not to report it.
            """
            res = bluetoothInbox.attachment_path(btMsgId)

            if res is None:
                return None

    return res


#def BluetoothMessageReadData(aBluetoothInbox, btMsgId):
def BluetoothMessageReadData(btMsgId):
    global bluetoothInbox

    btMsgData = None

    DebugPrint("BluetoothMessageReadData(): btMsgId = %s." % (btMsgId))

    """
    #Check that the BT message is sent by an iCam client.
    if fileNameBtMsg[0 : len(BT_OBEX_FILENAME_PREFIX)]
            != BT_OBEX_FILENAME_PREFIX:
        DebugPrint("BluetoothMessageProcessAndDelete(): " \
                    "fileNameBtMsg does not start " \
                    "with %s. Bailing out without processing " % \
                    "or erasing the message..." \
                    BT_OBEX_FILENAME_PREFIX)
        return
    """

    if ANDROID_OS or (SYMBIAN_OS and ((S60_EDITION[0] < 3) or SYMBIAN_3)) or \
                                                    WINDOWS_OS or RASPBIAN_OS:
        pathFileNameBluetoothMessage = GetBluetoothMessagePathFileName(btMsgId)

        DebugPrint("BluetoothMessageReadData(): " \
            "pathFileNameBluetoothMessage=%s." % pathFileNameBluetoothMessage)

        try:
            fInput = None
            fInput = open(pathFileNameBluetoothMessage, "rb")
            btMsgData = fInput.read()
            fInput.close()
        except:
            # print(repr(fileNameBtMsg))

            # !!!!Alex
            # print(repr(BTMessageReadFile(fileNameBtMsg)))
            # Always prints "Bluetooth", presumably
            # print(repr(bluetoothInbox.address(btMsgId)))
            DebugPrintErrorTrace()

            btMsgData = None

            if fInput is not None:
                try:
                    fInput.close()
                except:
                    DebugPrintErrorTrace()

    elif SYMBIAN_OS:
        if S60_EDITION[0] >= 3:
            try:
                # MAX_READ_BT_MESSAGE = 512 * 1024
                MAX_READ_BT_MESSAGE = 64 * 1024
                sizeBluetoothMessage = bluetoothInbox.size(btMsgId)
            except:
                """
                Bad BT message.
                I got exception for a few BT messages on N95 transmitted in
                    Burst (Turbo) mode from N82. In this case I simply delete
                    this message.
                """
                DebugPrintErrorTrace()

                #return ""
                return None

            try:
                DebugPrint("BluetoothMessageReadData(): " \
                        "sizeBluetoothMessage = %d." % sizeBluetoothMessage)

                offsetBluetoothMessage = 0
                dataBluetoothMessage = []

                while offsetBluetoothMessage < sizeBluetoothMessage:
                    numBytesToReadBtMessage = sizeBluetoothMessage - \
                        offsetBluetoothMessage

                    if numBytesToReadBtMessage > MAX_READ_BT_MESSAGE:
                        numBytesToReadBtMessage = MAX_READ_BT_MESSAGE

                    s = bluetoothInbox.data(btMsgId, offsetBluetoothMessage,
                                                numBytesToReadBtMessage)

                    offsetBluetoothMessage += len(s)
                    dataBluetoothMessage.append(s)

                btMsgData = "".join(dataBluetoothMessage)
            except:
                DebugPrintErrorTrace()

                #return ""
                btMsgData = None

    if btMsgData != None:
        DebugPrint("BluetoothMessageReadData(): len(btMsgData) = %d." % \
                (len(btMsgData)))

    return btMsgData


btMsgStateTime = None # This is updated in GetInfoFromSMFBtMsg() and used by BluetoothTimeSyncWithDrift(), etc
def GetInfoFromSMFBtMsg(stateMediaData, fileName):
    global deviceIdFormat, btMsgStateTime
    global statePackFormat00, statePackFormat01, statePackFormat02, \
            statePackFormat03

    try:
        cameraId = int(fileName[len(fileName) - 5])

        """
        We want the time when the movie was recorded (and
            uploaded via BT on the server),
            NOT the upload time of the server.
        """
        #crtTime = GetCurrentDateTime()

        # We get the time the file was uploaded from the state.
        #statePackFormat03_1 = statePackFormat03[1:]
        statePackFormat03_1 = statePackFormat03[1:] + statePackFormat03b[1:]

        if NEW_BT_FORMAT:
            """
            Note: we require the little endian sign "<" before the other format
                strings.
            """
            statePackFormatPartial = "<" + statePackFormat00[1:] + \
                                statePackFormat01[1:] + statePackFormat02[1:]

            statePackFormatPartial2 = "<" + statePackFormat00[1:] + \
                                statePackFormat01[1:]
        else:
            """
            Note: we require the little endian sign "<" before the other
              format strings
                - that is why we have the "<" in the deviceIdFormat string
            """
            statePackFormatPartial = deviceIdFormat + statePackFormat00[1:] + \
                                statePackFormat01[1:] + statePackFormat02[1:]

        if NEW_BT_FORMAT:
            offset = len(stateMediaData) - struct.calcsize(statePackFormat)
            offset2 = len(stateMediaData) - struct.calcsize(statePackFormat)
        else:
            offset = 0
            offset2 = 0

        DebugPrint("GetInfoFromSMFBtMsg(): offset " \
            "(where iCam state data begins) = %d (0x%x)" % (offset, offset))

        offset += struct.calcsize(statePackFormatPartial)
        offset2 += struct.calcsize(statePackFormatPartial2)

        DebugPrint("GetInfoFromSMFBtMsg(): " \
                    "offset = %d (0x%x), offset2 = %d." % \
                                    (offset, offset, offset2))

        """
        DebugPrint("BluetoothMessageProcess:" \
                    "UploadBluetoothMediaToGoogle(): " \
                    "statePackFormat03_1 = %s, " \
                    "calcsize() = %d." % \
                    (statePackFormat03_1,
                    struct.calcsize(
                        statePackFormatPartial)))
        #DebugPrint("BluetoothMessageProcess:" \
        #       "UploadBluetoothMediaToGoogle(): " \
        #       "tm_year = %s" % str(tm_year))
        """

        (tm_year, tm_mon, tm_mday,
                    tm_hour, tm_min, tm_sec,
                    numMilliseconds,
                    btMsgStateTime, _, _) = \
            struct.unpack(statePackFormat03_1,
                            stateMediaData[offset : offset + \
                            struct.calcsize(statePackFormat03_1)])

        statePackFormat02_1 = statePackFormat02[1:]
        btAddr = [0, 0, 0, 0, 0, 0]
        (_, btAddr[0], btAddr[1], btAddr[2], btAddr[3], btAddr[4], btAddr[5],
            _ , _) = struct.unpack(statePackFormat02_1,
                                    stateMediaData[offset2 : offset2 + \
                                        struct.calcsize(statePackFormat02_1)])
        btCliendMACAddrList = ["%02X:" % e for e in btAddr]
        btCliendMACAddr = "".join(btCliendMACAddrList)
        btCliendMACAddr = btCliendMACAddr[: len(btCliendMACAddr) - 1]

        timeTuple = (tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec,
                        numMilliseconds)

        DebugPrint("GetInfoFromSMFBtMsg(): cameraId = %d, " \
                "tm_year = %d, tm_mon = %d, tm_mday = %d, " \
                "tm_hour = %d, tm_min = %d, tm_sec=%d, numMilliseconds=%d, " \
                "btMsgStateTime = %d." % \
                    (cameraId,
                        tm_year, tm_mon, tm_mday,
                        tm_hour, tm_min, tm_sec, numMilliseconds,
                        btMsgStateTime))


        offset3 = offset + struct.calcsize(statePackFormat03_1)
        statePackFormatPartial += statePackFormat03_1
        statePackFormat04_1 = statePackFormat04[1:]

        aStr = stateMediaData[offset3 : offset3 + \
                            struct.calcsize(statePackFormat04)]
        DebugPrint("GetInfoFromSMFBtMsg(): " \
                "struct.calcsize(statePackFormat04) = %d, " \
                "len(aStr) = %d." % \
                    (struct.calcsize(statePackFormat04), len(aStr)))

        (aBatteryLevel, _, _, _, _, aChargerStatus) = \
            struct.unpack(statePackFormat04, aStr)
        DebugPrint("GetInfoFromSMFBtMsg(): " \
                "aBatteryLevel = %d, aChargerStatus = %d." % \
                    (aBatteryLevel, aChargerStatus))

        return (cameraId, timeTuple, btCliendMACAddr, \
                          aBatteryLevel, aChargerStatus)
    except:
        DebugPrintErrorTrace()
        return None


def GetMediaFromBluetoothSMFPacket(stateMediaData):
    try:
        lenNonMediaData = struct.calcsize(deviceIdFormat) + \
                                    struct.calcsize(statePackFormat)

        if NEW_BT_FORMAT:
            mediaData = stateMediaData[ : -lenNonMediaData]
        else:
            mediaData = stateMediaData[lenNonMediaData : ]

        return mediaData
    except:
        DebugPrintErrorTrace()


def UploadBluetoothMediaToGoogle(stateMediaData, fileName,
                    fileNameExtensionList, uploadFunction, btClientDeviceId,
                    aBatteryLevel=-1, aChargerStatus=-1):

    fileNameExtension = str.lower(fileName[len(fileName) - 4:])

    #TODO!!!!!!!!: takeout this info and pass it directly to UploadBluetoothMediaToGoogle()
    res = GetInfoFromSMFBtMsg(stateMediaData, fileName)
    if res is None:
        return

    cameraId = res[0]
    (tm_year, tm_mon, tm_mday,
        tm_hour, tm_min, tm_sec, numMilliseconds) = res[1]

    DebugPrint("Entered UploadBluetoothMediaToGoogle().")
    #DebugPrint("len(fileName) =", len(fileName))

    if fileNameExtension in fileNameExtensionList:
        DebugPrint("UploadBluetoothMediaToGoogle(): uploading.")
        #DebugPrint("len(fileName) = %d" % len(fileName))

        fOutput = None

        try:
            """
            # Unfortunately time.mktime() DOES NOT WORK ON
            #   PyS60 2.0. It simply crashes it without an
            #   exception...
            #From http://www.tutorialspoint.com/python/time_mktime.htm

            # The last 3 args are tm_wday, tm_yday,tm_isdst
            btMediaTime = time.localtime(time.mktime(
                (tm_year, tm_mon, tm_mday, tm_hour, tm_min,
                tm_sec, -1, -1, -1) ))
            """

            # btMediaTimeStr = "%02d:%02d:%02d.%03d " \
            #   "%02d-%02d-%04d" % (tm_hour, tm_min,
            #   tm_sec, numMilliseconds, tm_mday, tm_mon,
            #   tm_year)
            btMediaTimeStr = "%02d:%02d:%02d.%01d %02d-%02d-%04d" % \
                (
                    tm_hour, tm_min, tm_sec,
                    int((numMilliseconds + 50) / 100),
                    tm_mday, tm_mon, tm_year
                )

            btMediaDateStr = "%04d-%02d-%02d" % (tm_year, tm_mon, tm_mday)

            DebugPrint("UploadBluetoothMediaToGoogle(): " \
                        "btMediaTimeStr = %s, btMediaDateStr = %s." % \
                        (btMediaTimeStr, btMediaDateStr))
            #DebugPrint("len(fileName) = %d" % len(fileName))

            #pathFileName = "D:/" + fileName
            pathFileName = LOCAL_FOLDER_TEMP + "/" + fileName
            fOutput = open(pathFileName, "wb")
            mediaData = GetMediaFromBluetoothSMFPacket(stateMediaData)
            fOutput.write(mediaData)

            """
            uploadFunction(pathFileName, fileName, \
                            btClientDeviceId, crtTime, \
                            btClientDeviceId, cameraId)
            """
            def MyThreadUploadFunction():
                uploadFunction(pathFileName, fileName, btClientDeviceId, None,
                                btMediaTimeStr, btMediaDateStr,
                                btClientDeviceId, cameraId,
                                aBatteryLevel, aChargerStatus)

                if fOutput is not None:
                    try:
                        fOutput.close()
                    except:
                        DebugPrintErrorTrace()

                    try:
                        os.unlink(pathFileName)
                    except:
                        # Maybe the file got moved since
                        #    uploading to Google failed.
                        DebugPrintErrorTrace()

            MyThreadStart(MyThreadUploadFunction)
        except IOError:
            DebugPrintErrorTrace()

            """
            We assume we obtain exception:
              "IOError: [Errno 28] No space left on " \
               "device: 'D:/2011_04_25_23_17_39_032_0.3gp'"

            This is probably related to the fact YouTube
                upload did not succeed and the video file
                in LOCAL_FOLDER_TEMP was not erased
                probably because it was hanged by the
                YouTube API.
            """
            RestartPhone()
        except:
            DebugPrintErrorTrace()
        """
        # Doesn't work on Python 2.2 (e.g., PyS60 1.4.5)
        finally:
            try:
                fOutput.close()
            except:
                DebugPrintErrorTrace()
            try:
                os.unlink(pathFileName)
            except:
                DebugPrintErrorTrace()
        """


def BluetoothMessageProcessTXT(btMsgId, btMsgData,
                                fileNameBtMsg, btClientDeviceId):

    DebugPrint("BluetoothMessageProcessTXT(btMsgId = %s): Processing a text " \
        "message from btClientDeviceId = %s of len(btMsgData) = %d, " \
        "fileNameBtMsg = %s - saving it in the Unsent folder." % \
        (btMsgId, btClientDeviceId, len(btMsgData), fileNameBtMsg))

    """
    fOutput = open(LOCAL_FOLDER_MEDIA_FILES + "/" + fileNameBtMsg +\
                    "_before.dgb", "wb")
    fOutput.write(btMsgData)
    fOutput.close()
    """

    try:
        if NEW_BT_FORMAT:
            #fileName = fileNameBtMsg[len(BT_OBEX_FILENAME_PREFIX): ]
            fileName = fileNameBtMsg[len(BT_OBEX_FILENAME_PREFIX): \
                                        -len(BT_OBEX_EXTENSION_TXT)] + \
                        EXTENSION_TEXT_MESSAGE
        else:
            fileName = fileNameBtMsg[len(BT_OBEX_FILENAME_PREFIX) + \
                                    len(BT_OBEX_FILENAME_PREFIX_TYPE_TXT):] + \
                        EXTENSION_TEXT_MESSAGE
        """
        fileName = time.strftime("%Y_%m_%d_%H_%M_%S_",
            GetCurrentDateTime()) + EXTENSION_TEXT_MESSAGE
        """

        DebugPrint("BluetoothMessageProcessTXT(): fileName = %s." % fileName)

        if NEW_BT_FORMAT:
            """
            # ~Deprecated
            # We add the footer "data-link layer" packet info as header to be an Internet packet.
            lenExtra = struct.calcsize(deviceIdFormat)

            dataToSend = btMsgData[-lenExtra : ] + \
                            btMsgData[ : -lenExtra]
            """
            dataToSend = btMsgData

            """
            fOutput = open(LOCAL_FOLDER_MEDIA_FILES + "/" + \
                            fileNameBtMsg + "_after.dgb", "wb")
            fOutput.write(dataToSend)
            fOutput.close()
            """

            """
            The packet dataToSend == btMsgData ALREADY has a
                "data-link layer" 100-bytes-long header
                with deviceId, followed by the "payload" text message.
              Therefore to obtain a packet to send to the iCam server
                we only need to zlib-compress the packet.
            """
            dataToSend = dataToSend.encode("zlib")

            UploadUnsentBinaryData(btClientDeviceId, fileName, dataToSend)
        else:
            """
            fOutput = open(pathName + "/" + fileName, "wb")
            fOutput.write(btMsgData[4 : ])
            fOutput.close()
            """
            UploadUnsentBinaryData(btClientDeviceId, fileName, btMsgData[4:])

        # bluetoothInbox.delete(btMsgId)
        BluetoothDeleteMessage(btMsgId)
    except:
        DebugPrintErrorTrace()


def BluetoothMessageProcessFIL(btMsgId, btMsgData,
                                fileNameBtMsg, btClientDeviceId):
    # """
    DebugPrint("BluetoothMessageProcessFIL(): Processing a file message " \
                "from %s - saving it in the Unsent folder." % btClientDeviceId)
    # """

    try:
        if NEW_BT_FORMAT:
            fileName = fileNameBtMsg[len(BT_OBEX_FILENAME_PREFIX): ]
        else:
            fileName = fileNameBtMsg[len(BT_OBEX_FILENAME_PREFIX) + \
                        len(BT_OBEX_FILENAME_PREFIX_TYPE_TXT):] + \
                        EXTENSION_ARBITRARY_FILE

        if NEW_BT_FORMAT:
            UploadUnsentBinaryData(btClientDeviceId, fileName, btMsgData)
        else:
            UploadUnsentBinaryData(btClientDeviceId, fileName, btMsgData[4:])
        """
        #fileName = time.strftime("%Y_%m_%d_%H_%M_%S_", \
        #   GetCurrentDateTime()) + EXTENSION_TEXT_MESSAGE

        fOutput = open(pathName + "/" + fileName, "wb")
        fOutput.write(btMsgData[4 : ])
        fOutput.close()
        """

        # bluetoothInbox.delete(btMsgId)
        BluetoothDeleteMessage(btMsgId)
    except:
        DebugPrintErrorTrace()


def BluetoothMessageProcessSMF(btMsgId, btMsgData, stateMediaData,
                                fileNameBtMsg, btClientDeviceId,
                                aBatteryLevel=-1, aChargerStatus=-1):

    DebugPrint(
        "Entered BluetoothMessageProcessSMF(btMsgId=%s, btMsgData, " \
        "stateMediaData of len %d, fileNameBtMsg=%s, btClientDeviceId=%s, " \
        "aBatteryLevel=%d, aChargerStatus=%d)." % \
                (btMsgId, len(stateMediaData), fileNameBtMsg, btClientDeviceId,
                                            aBatteryLevel, aChargerStatus))

    """
    fOutput = open(LOCAL_FOLDER_MEDIA_FILES + "/" + fileNameBtMsg + ".dgb", "wb")
    fOutput.write(stateMediaData)
    fOutput.close()
    """

    try:
        """
        # Write on mem card temporary file from received BT.
        # We need to do this only for debugging purposes - so better
        #   comment all this block if not needed.

        tmpPathFileName = LOCAL_FOLDER_MEDIA_FILES + "/" + \
                            BT_OBEX_FILENAME_PREFIX + "_rcvd"

        fOutput = open(tmpPathFileName, "wb")
        fOutput.write(btMsgData)
        fOutput.close()
        """

        #ReceiveArchivedFile()

        #stateMediaData = bluetoothFD.read(1000000)

        #appuifw.note(u"ReceiveArchivedFile: read %d bytes." %
        #   (fileDataSize + 4), "info")

        #fOutput = open("bla_tmp_after", "wb")
        #fOutput.write(stateMediaData)
        #fOutput.close()

        """
        TODO!!!!: should I declare gpsInfo as global? Why am I
            actually clearing gpsInfo?
        gpsInfo = {"position": {}, "course": {}, "satellites": {}}
        """

        """
        Since I only want to obtain fileName, I extract it directly
            without unpack().
        """
        if NEW_BT_FORMAT:
            # We compute the offset to the fileName in the iCam state
            #offset = len(stateMediaData) - struct.calcsize(statePackFormat) + 1408
            """
            # These 2 cancel:
            offset = len(stateMediaData) - \
                    struct.calcsize(statePackFormat) + \
                    struct.calcsize(statePackFormat) - \
                    struct.calcsize(statePackFormat26) - \
                    struct.calcsize(statePackFormat27)
            """
            offsetFileName = len(stateMediaData) - \
                    struct.calcsize(statePackFormat26) - \
                    struct.calcsize(statePackFormat27) - \
                    struct.calcsize(statePackFormat28)

            #fileName = stateMediaData[offset: offset + 256] #+ "_" + btClientDeviceId + cameraId
            fileName = stateMediaData[offsetFileName: offsetFileName + \
                                        struct.calcsize(statePackFormat26)]
        else:
            OFFSET_FILENAME_IN_STATE = 1408

            fileName = stateMediaData[struct.calcsize(deviceIdFormat) + \
                OFFSET_FILENAME_IN_STATE : struct.calcsize(deviceIdFormat) + \
                OFFSET_FILENAME_IN_STATE + 256]

        firstNullCharIndex = fileName.find("\x00")
        fileName = fileName[0 : firstNullCharIndex]

        DebugPrint("BluetoothMessageProcessSMF(): fileName " \
                    "(from state) = %s." % fileName)
        #DebugPrint("len(fileName) = %d" % len(fileName))

        """
        if not os.path.exists(LOCAL_FOLDER_UNSENT_FILES):
            os.makedirs(LOCAL_FOLDER_UNSENT_FILES)

        pathName = LOCAL_FOLDER + "/" + btClientDeviceId
        if not os.path.exists(pathName):
            os.makedirs(pathName)

        pathName = LOCAL_FOLDER + "/" + btClientDeviceId + "/Unsent"
        if not os.path.exists(pathName):
            os.makedirs(pathName)
        """

        if uploadMediaToYouTube or uploadMediaToPicasa:
            """
            Note that UploadBluetoothMediaToGoogle() creates a separate thread
                to handle the Google upload. This is OK, since we create the
                file to be uploaded before entering the thread and delete it
                in the thread, when we finish.
            """
            if uploadMediaToYouTube:
                UploadBluetoothMediaToGoogle(stateMediaData, fileName,
                        [".3gp", ".mp4"], YouTubeVideoUpload, btClientDeviceId,
                        aBatteryLevel, aChargerStatus) #-1, -1)
                """
                SleepAndPetWatchdog(pauseIntervalGdata)
                """

            if uploadMediaToPicasa:
                UploadBluetoothMediaToGoogle(stateMediaData, fileName,
                        [".jpg", ".png"], PicasaPhotoUpload, btClientDeviceId)
                """
                SleepAndPetWatchdog(pauseIntervalGdata)
                """

        if not ERASE_ORIGINAL_MEDIA_FILE_AFTER_READ:
            # Store the media file from the Bluetooth received message.
            pathName = LOCAL_FOLDER_MEDIA_FILES + "/" + btClientDeviceId

            if not os.path.exists(pathName):
                os.makedirs(pathName)

            mediaData = GetMediaFromBluetoothSMFPacket(stateMediaData)
            fOutput = open(pathName + "/" + fileName, "wb")
            fOutput.write(mediaData)
            fOutput.close()

        if NEW_BT_FORMAT:
            pass
        else:
            """
            if (fileName[len(fileName) - 4 : ] == ".jpg") or
                (fileName[len(fileName) - 4 : ] == ".3gp"):
            """
            # Could be ".3gp", ".jpg", ".png", etc.
            if fileName[len(fileName) - 4] == ".":
                fileName = fileName[:len(fileName) - 4] + \
                    EXTENSION_STATE_AND_MEDIA_FILE

        lenNonMediaData = struct.calcsize(deviceIdFormat) + \
                                    struct.calcsize(statePackFormat)

        # Note that we consider the iCam server the BT proxy
        if useiCamServer == 2:
            if NEW_BT_FORMAT:
                fileName = fileName[ : -4] + EXTENSION_STATE_AND_MEDIA_FILE

                SMFData = stateMediaData[-lenNonMediaData : ] + \
                            stateMediaData[ : -lenNonMediaData]

                """
                fOutput = open(LOCAL_FOLDER_UNSENT_FILES + "/" + fileName + "2.dbg", "wb")
                fOutput.write(SMFData)
                fOutput.close()
                """

                SMFData = SMFData.encode("zlib")

                UploadUnsentBinaryData(btClientDeviceId, fileName, SMFData)
            else:
                #btMsgData[4:] is the SMF packet, compressed. stateMediaData is btMsgData[4:].decode("zlib")
                UploadUnsentBinaryData(btClientDeviceId, fileName,
                                                btMsgData[4:])
        elif useiCamServer == 1:
            """
            # We send ONLY THE STATE data from the Bluetooth message,
            #   to the iCam Server
            stateData = stateMediaData[struct.calcsize(deviceIdFormat) : \
                                struct.calcsize(deviceIdFormat) + \
                                struct.calcsize(statePackFormat)]
            """
            if NEW_BT_FORMAT:
                fileName = fileName[ : -4] + EXTENSION_STATE_AND_MEDIA_FILE
                stateData = stateMediaData[-lenNonMediaData : ]
            else:
                stateData = stateMediaData[0: lenNonMediaData]

            stateDataCompressed = stateData.encode("zlib")

            UploadUnsentBinaryData(btClientDeviceId, fileName,
                                            stateDataCompressed)

        # """
        if uploadMediaToYouTube or uploadMediaToPicasa:
            """
            Wait less if useiCamServer and size of packet
                is big (e.g., 50-200KB).
            """
            SleepAndPetWatchdog(pauseIntervalGdata)
        # """

        """
        fOutput = open(pathName + "/" + fileName, "wb")
        fOutput.write(btMsgData[4 : ])
        fOutput.close()
        """

        """
        firstNullCharIndex = accessPointName.find("\x00")
        accessPointName = accessPointName[0 : firstNullCharIndex]
        #print "len(fileName) =", len(fileName)
        accessPointName = unicode(accessPointName)
        """

        # appuifw.note(u"Wrote received file %s." % fileName, "info")
        # bluetoothServerTimer.after(1, \
        #    BluetoothMessageListProcessWrapper(bluetoothFD))

        # bluetoothInbox.delete(btMsgId)
        BluetoothDeleteMessage(btMsgId)
        """
        !!!!If we receive "SymbianError: [Errno -14] KErrInUse"
            (see http://mobile-revival.110mb.com/ReVival/N95N95N95N95N95/FromPhone/stderr_2011_06_13_10_02_41.txt)
            try to Quit() or restart phone
        """
    except:
        DebugPrintErrorTrace()


def BluetoothMessageProcessCMD(btMsgId, btMsgData, aData,
                                fileNameBtMsg, btClientDeviceId):

    DebugPrint("Entered BluetoothMessageProcessCMD(btMsgId=%s, btMsgData of " \
        "len %d, aData of len %d, fileNameBtMsg=%s, btClientDeviceId=%s." % \
        (btMsgId, len(btMsgData), len(aData), fileNameBtMsg, btClientDeviceId))

    """
    Delete before ExecuteCommands() because we might execute a
        RestartPhone().
    """
    # bluetoothInbox.delete(btMsgId)
    BluetoothDeleteMessage(btMsgId)

    """
    fileName = fileNameBtMsg[len(BT_OBEX_FILENAME_PREFIX) +
                len(BT_OBEX_FILENAME_PREFIX_TYPE_TXT) :] +
                EXTENSION_TEXT_MESSAGE
    """


    """
    If it's not a BT client (i.e., it is a BT server or
        a standard node connected to Inet) it doesn't make that
        much sense normally to receive BT messages.
      But at least for testing purposes we allow also the BT server
        to process BT CMD messages.
    """
    # BT client
    if (bluetoothMode == 2) or (bluetoothMode == 1):
        if NEW_BT_FORMAT:
            cmdString = aData[struct.calcsize(deviceIdFormat): ]
            #cmdString = aData
            """
            # If ever planning to send this file to the Inet server (why?) do:
            fileName = fileNameBtMsg[len(BT_OBEX_FILENAME_PREFIX): \
                                        -len(BT_OBEX_EXTENSION_CMD)] + \
                        EXTENSION_COMMAND_MESSAGE
            """
        else:
            cmdString = aData[struct.calcsize(deviceIdFormat): ]

        """
        DebugPrint("BluetoothMessageProcessCMD(): cmdString = %s" % cmdString)
        """

        # We create a backup file of the commands received.
        fileName = COMMANDS_FILENAME + \
                            GetCurrentDateTimeStringWithMilliseconds()
        fOutput = open(LOCAL_FOLDER + "/" + fileName, "wb")
        fOutput.write(cmdString)
        fOutput.close()
        ExecuteCommands(cmdString)



if not (SYMBIAN_S60_OS and (_PyS60_1_9_OR_NEWER == False)):
    # Python 2.2 doesn't have the pickle module
    try:
        import pickle
    except:
        DebugPrintErrorTrace()

#if SYMBIAN_OS:
#BT_MAP_FILENAME = LOCAL_FOLDER_TEMP + "/BT_map.pkl"
BT_MAP_FILENAME = LOCAL_FOLDER + "/BT_map.pkl"

def LoadBtMsgMostRecentTime():
    global btMsgMostRecentTime, btAddrTable

    if SYMBIAN_S60_OS:
        if _PyS60_1_9_OR_NEWER == False:
            """
            Python 2.2 doesn't have the pickle module.
                Anyhow we hope not to use it for iCam BT server.
            """
            return

    fInput = None

    try:
        if os.path.isfile(BT_MAP_FILENAME):
            # Inspired from second example at 
            #    http://docs.python.org/library/pickle.html at 11.1.7:
            fInput = open(BT_MAP_FILENAME, "rb")

            btMsgMostRecentTime = pickle.load(fInput)
            btAddrTable = pickle.load(fInput)

            fInput.close()

            DebugPrint("LoadBtMsgMostRecentTime(): btMsgMostRecentTime = %s, " \
                            "btAddrTable = %s" % \
                            (str(btMsgMostRecentTime), str(btAddrTable)))
    except:
        try:
            if fInput is not None:
                fInput.close()
        except:
            DebugPrintErrorTrace()

        DebugPrintErrorTrace()


def StoreBtMsgMostRecentTime():
    if SYMBIAN_S60_OS:
        if _PyS60_1_9_OR_NEWER == False:
            """
            Python 2.2 doesn't have the pickle module.
                Anyhow we hope not to use it for iCam BT server.
            """
            return

    fOutput = None

    try:
        # Inspired from first example at
        #    http://docs.python.org/library/pickle.html at 11.1.7:
        fOutput = open(BT_MAP_FILENAME, "wb")

        # Pickle using protocol 0.
        pickle.dump(btMsgMostRecentTime, fOutput)
        pickle.dump(btAddrTable, fOutput)

        fOutput.close()
    except:
        try:
            if fOutput is not None:
                fOutput.close()
        except:
            DebugPrintErrorTrace()

        DebugPrintErrorTrace()


"""
Dictionary with key (deviceId, cameraId) and
    value media time (time.mktime() or tuple, depending on the OS).
"""
btMsgMostRecentTime = {}

"""
Dictionary holding for a BT server iCam phone
    the key deviceId and value Bluetooth MAC address of all BT clients.
"""
btAddrTable = {}

"""
btMsgId is:
    - for ANDROID_OS, WINDOWS_OS and SYMBIAN_3: a string representing the (filename) name of the BT msg
    - for other SYMBIAN_OS: an integer ID for the messages from the BT Inbox.
"""
def BluetoothMessageProcessAndDelete(btMsgId, deleteMessageIfInvalid=False):
    global bluetoothInbox
    global statePackFormat
    global btMsgList

    if ANDROID_OS or WINDOWS_OS or RASPBIAN_OS:
        """
        fIndex = btMsgId.rfind("/") #!!!!maybe \
        fileNameBtMsg = btMsgId[fIndex + 1:]
        pathFileNameBluetoothMessage = btMsgId
        """
        fileNameBtMsg = GetBluetoothMessageName(btMsgId)
        pathFileNameBluetoothMessage = GetBluetoothMessagePathFileName(btMsgId)
        sizeBluetoothMessage = os.path.getsize(pathFileNameBluetoothMessage)
        timeBluetoothMessage = GetTime()
        btMsgArrivalTime = -1
    elif SYMBIAN_OS:
        fileNameBtMsg = ""
        sizeBluetoothMessage = -1

        try:
            fileNameBtMsg = GetBluetoothMessageName(btMsgId)

            if SYMBIAN_3:
                #fileNameBtMsg = str(btMsgId)
                sizeBluetoothMessage = os.path.getsize( \
                                    GetBluetoothMessagePathFileName(btMsgId))
            else:
                if S60_EDITION[0] >= 3:
                    try:
                        sizeBluetoothMessage = bluetoothInbox.size(btMsgId)
                    except:
                        DebugPrintErrorTrace()
                        #!!!!TODO check specifically for "SymbianError: [Errno -20] KErrCorrupt" and only then do BluetoothDeleteMessage
                        BluetoothDeleteMessage(btMsgId, True)
                        return -1

            # Get message time of arrival.
            btMsgArrivalTime = bluetoothInbox.time(btMsgId)
        except:
            DebugPrintErrorTrace()

    myText = "Entered BluetoothMessageProcessAndDelete(btMsgId = %s) on %s " \
                "(fileNameBtMsg = %s, " \
                "sizeBluetoothMessage = %d, btMsgArrivalTime = %s). " \
                "(Inbox has %d Bluetooth messages.)" % \
                (str(btMsgId), GetCurrentDateTimeStringNice(),
                    fileNameBtMsg, sizeBluetoothMessage,
                    str(btMsgArrivalTime),
                    len(GetBluetoothInboxMessageList()))
    DebugPrint(myText)

    try:
        btMsgArrivalTimeStruct = time.localtime(btMsgArrivalTime)
        DebugPrint(
            "    BluetoothMessageProcessAndDelete(): btMsgArrivalTimeStruct = " + \
            time.strftime("%Y_%m_%d_%H_%M_%S", btMsgArrivalTimeStruct))
    except:
        DebugPrintErrorTrace()

    ###########################################################################
    ###########################################################################
    ##########Now we have set fileNameBtMsg and sizeBluetoothMessage###########
    ###########################################################################
    ###########################################################################

    #!!!!TODO: Alex
    """
    if MY_DEBUG_UPLOAD_MSG:
        UploadGZippedData(deviceId, myText, 
                            ICAM_SERVER_NAME, WEBPAGE_UL_GZIPPED_TEXT, None)
    """

    """
    print("message %s" % str(btMsgId))

    if bluetoothInbox.unread(btMsgId):
        print "unread"
    else:
        print "read"

    print("message type is %x" % bluetoothInbox.message_type(btMsgId))
    """
    try:
        DebugPrint("BluetoothMessageProcessAndDelete(): " \
                    "fileNameBtMsg = %s." % fileNameBtMsg)

        # Check that the BT message is sent by an iCam client.
        if not fileNameBtMsg.startswith(BT_OBEX_FILENAME_PREFIX):
            DebugPrint("BluetoothMessageProcessAndDelete(): " \
                "fileNameBtMsg = %s does not start " \
                "with %s, so we consider it not being an " \
                "iCam BT message. Bailing out without " \
                "processing or erasing the message..." % \
                    (fileNameBtMsg, BT_OBEX_FILENAME_PREFIX))

            return -1

        # sizeBluetoothMessage = bluetoothInbox.size(btMsgId)
        #btMsgData = BluetoothMessageReadData(bluetoothInbox, btMsgId)
        btMsgData = BluetoothMessageReadData(btMsgId)

        if btMsgData is None:
            #!!!!TODO check specifically for "SymbianError: [Errno -20] KErrCorrupt" and only then do BluetoothDeleteMessage
            BluetoothDeleteMessage(btMsgId, True)
            return -1
    except:
        #print("size is %d" % sizeBluetoothMessage)
        #print(repr(BluetoothMessageReadData(bluetoothInbox, btMsgId)))
        DebugPrintErrorTrace()
        BluetoothDeleteMessage(btMsgId, True)
        return -1

    ###########################################################################
    ###########################################################################
    #####Now we have read entirely the Bluetooth message in btMsgData##########
    ###########################################################################
    ###########################################################################

    # Checking integrity of the BT msg received and reading vars from header.
    try:
        #if btMsgData == "":
        if len(btMsgData) < 4 + 1:
            DebugPrint("BluetoothMessageProcessAndDelete(): len(btMsgData) = %d. " \
                    "Too small, so we consider it invalid and delete " \
                    "the message. Bailing out..." % len(btMsgData))

            """
            if deleteMessageIfInvalid:
                #bluetoothInbox.delete(btMsgId)
                BluetoothDeleteMessage(btMsgId)
            """
            BluetoothDeleteMessage(btMsgId, True)

            return -1

        if NEW_BT_FORMAT == False:
            ####################################################################
            ####################################################################
            ####Now we check the first word of btMsgData == fileDataSize####
            ####################################################################
            ####################################################################

            # !!!!Be careful at endianess.
            # fileDataSize = int(bluetoothFD.read(4))
            """
            IMPORTANT: We require < to specify little endian and ALSO no
                alignment (see http://docs.python.org/library/struct.html).
            """
            (fileDataSize, ) = struct.unpack("<i", btMsgData[0 : 4])

            DebugPrint("BluetoothMessageProcessAndDelete(): " \
                        "fileDataSize=%s." % fileDataSize)

            if len(btMsgData) - 4 != fileDataSize:
                DebugPrint("BluetoothMessageProcessAndDelete(): " \
                    "len(btMsgData) - 4 != fileDataSize. " \
                    "So, it appears this Bluetooth message was " \
                    "not sent by an iCam Bluetooth client. " \
                    "len(btMsgData) = %d, fileDataSize = %d. " \
                    "Bailing out..." % (len(btMsgData), fileDataSize))

                """
                We delete it (or store it in an Error folder, if BT messages are
                    DIRECTLY accessible via the FS (Symbian 3+, Android)
                """
                BluetoothDeleteMessage(btMsgId, True)

                return -1

        """
        NOTE THAT THE CHECK WE DO IS NOT RELIABLE
        It seems the magic byte 0x78 is always there
            - when using zlib.compressobj(1).compress() we have 0x78 0x01
            - when using encode("zlib") we have 0x78 0x9C

        # Check for Gzip stream header.
        if not (btMsgData[4] == 0x78 and stateMediaData[
                                        btMsgData[5] == 0x9C):
            return
        """
        DebugPrint("BluetoothMessageProcessAndDelete(): " \
                    "len(btMsgData) = %d" % len(btMsgData))

        #######################################################################
        ###Unzipping, unpacking the deviceId and creating the corresponding####
        ###  Unsent folder for the deviceId.###################################
        #######################################################################
        """
        if fileNameBtMsg[len(BT_OBEX_FILENAME_PREFIX) : \
                                len(BT_OBEX_FILENAME_PREFIX) + \
                                len(BT_OBEX_FILENAME_PREFIX_TYPE_FIL)] == \
                                BT_OBEX_FILENAME_PREFIX_TYPE_FIL:
        """

        if (NEW_BT_FORMAT and
                        fileNameBtMsg.endswith(EXTENSION_ARBITRARY_FILE)) or \
            (not NEW_BT_FORMAT and \
                        fileNameBtMsg[len(BT_OBEX_FILENAME_PREFIX) : ].\
                                startswith(BT_OBEX_FILENAME_PREFIX_TYPE_TXT)):

            # Arbitrary file (Not SMF, nor CMD, nor TXT)
            """
            It is very possible that the uncompressed data can be huge so we
                use zlib.decompressobj to partially decompress.
            """
            zlibDecompressObject = zlib.decompressobj()

            if NEW_BT_FORMAT:
                stateMediaDataCompressed = btMsgData
            else:
                stateMediaDataCompressed = btMsgData[4:]

            # See http://docs.python.org/library/zlib.html .
            stateMediaData = zlibDecompressObject.decompress(
                                stateMediaDataCompressed,
                                struct.calcsize(statePackFormat) + 1000)
            """

            # Maybe a few times with max_length = 8 * 1024
            stateMediaData += zlibDecompressObject.decompress(
                                zlibDecompressObject.unconsumed_tail)
            stateMediaData += zlibDecompressObject.flush()
            """

            # Treat well!!!! - put in outter block
            if NEW_BT_FORMAT:
                #!!!!
                pass
            # !!!!TODO if suffix _icam.jpg then don't need to decode
        # elif ():
        else:
            if NEW_BT_FORMAT:
                stateMediaData = btMsgData
            else:
                """
                This treats SMF, CMD, and TXT.
                Note that for CMD and SMF the uncompressed data is roughly the
                    same size as the compressed one.
                """
                # stateMediaData = btMsgData[4 : ].decode("zlib")
                stateMediaData = btMsgData[4:].decode("zlib")

    ###########################################################################
    ###########################################################################
    ##Now we have in stateMediaData the state data and file data (if required)##
    ###########################################################################
    ###########################################################################

        if NEW_BT_FORMAT:
            # This assumes the deviceId has 15 chars
            #btClientDeviceId = fileNameBtMsg[-21:-6]

            btClientDeviceId = fileNameBtMsg.split("_")[-2]
        else:
            """
            IMPORTANT NOTE: we use "<" to specify no alignment -
                see http://docs.python.org/library/struct.html
            """
            (btClientDeviceId, ) = struct.unpack(deviceIdFormat,
                            stateMediaData[0 : struct.calcsize(deviceIdFormat)])

            firstNullCharIndex = btClientDeviceId.find("\x00")
            btClientDeviceId = btClientDeviceId[0 : firstNullCharIndex]

        """
        pathName = LOCAL_FOLDER_UNSENT_FILES + "/" + btClientDeviceId
        if not os.path.exists(pathName):
            os.makedirs(pathName)
        """

        DebugPrint("BluetoothMessageProcessAndDelete(): " \
                    "btClientDeviceId = %s." % btClientDeviceId)
        #DebugPrint("len(fileName) = %d" % len(fileName))
    except:
        try:
            if deleteMessageIfInvalid:
                # bluetoothInbox.delete(btMsgId)
                BluetoothDeleteMessage(btMsgId, True)
        except:
            DebugPrintErrorTrace()

        (exceptionType, exceptionValue, exceptionTraceback) = sys.exc_info()
        errorStr = "Exception in BluetoothMessageProcessAndDelete() with btMsgId = %s" \
                    " - details: free_ram = %d. exceptionTraceback = %s, " \
                    "exceptionType = %s, exceptionValue = %s. Bailing out..." % \
                    (str(btMsgId), GetFreeRAM(),
                       repr(traceback.format_tb(exceptionTraceback)),
                       str(exceptionType), str(exceptionValue))

        if MY_DEBUG_UPLOAD_MSG:
            UploadGZippedData(deviceId, errorStr, ICAM_SERVER_NAME,
                                        WEBPAGE_UL_GZIPPED_TEXT, None)

        DebugPrint("Exception in BluetoothMessageProcessAndDelete() " \
                    "with btMsgId = %s. Bailing out..." % str(btMsgId))

        DebugPrintErrorTrace()

        return -1

    ###########################################################################
    ###########################################################################
    ######Now, depending on file type, we process and delete the message.######
    ###########################################################################
    ###########################################################################
    # The order of processing is somewhat relevant - we have CMD BT messages
    #   with extension .cmd.txt and TXT BT messages with .txt extension, so we
    #   look first for CMD BT and then TXT BT messages.
    try:
        # If the message is CMD:
        #if (NEW_BT_FORMAT and str.lower(fileNameBtMsg[-4:])
        #        in BT_OBEX_EXTENSION_LIST_CMD) or \
        if (NEW_BT_FORMAT and 
                str.lower(fileNameBtMsg).endswith(BT_OBEX_EXTENSION_CMD)) or \
            (not NEW_BT_FORMAT and
                fileNameBtMsg[len(BT_OBEX_FILENAME_PREFIX): ]. \
                    startswith(BT_OBEX_FILENAME_PREFIX_TYPE_CMD)):
            BluetoothMessageProcessCMD(btMsgId, btMsgData, stateMediaData,
                                        fileNameBtMsg, btClientDeviceId)

        elif str.lower(fileNameBtMsg).endswith(BT_OBEX_EXTENSION_TIM):
            BluetoothMessageProcessTIM(btMsgId)
            
        # If the message is TXT:
        elif (NEW_BT_FORMAT and str.lower(fileNameBtMsg[-4:])
                    in BT_OBEX_EXTENSION_LIST_TXT) or \
            (not NEW_BT_FORMAT and 
                fileNameBtMsg[len(BT_OBEX_FILENAME_PREFIX): ]. \
                    startswith(BT_OBEX_FILENAME_PREFIX_TYPE_TXT)):

            BluetoothMessageProcessTXT(btMsgId, btMsgData,
                                fileNameBtMsg, btClientDeviceId)

        # If the message is FIL (contains an arbitrary file):
        elif (NEW_BT_FORMAT and str.lower(fileNameBtMsg[-4:])
                    in BT_OBEX_EXTENSION_LIST_FIL) or \
            (not NEW_BT_FORMAT and 
                fileNameBtMsg[len(BT_OBEX_FILENAME_PREFIX): ]. \
                    startswith(BT_OBEX_FILENAME_PREFIX_TYPE_FIL)):

            BluetoothMessageProcessFIL(btMsgId, btMsgData,
                                fileNameBtMsg, btClientDeviceId)

        # If the message is SMF (contains state and media file):
        #elif str.lower(fileNameBtMsg[len(fileNameBtMsg)
        #               - 4:]) in [".jpg", ".png", ".3gp", ".mp4"]:
        elif (NEW_BT_FORMAT and str.lower(fileNameBtMsg[-4:])
                in BT_OBEX_EXTENSION_LIST_SMF) or \
            (not NEW_BT_FORMAT and
                fileNameBtMsg[len(BT_OBEX_FILENAME_PREFIX): ]. \
                    startswith(BT_OBEX_FILENAME_PREFIX_TYPE_SMF)):

            res = GetInfoFromSMFBtMsg(stateMediaData, fileNameBtMsg)

            DebugPrint(
                "BluetoothMessageProcessAndDelete(): res = %s." % (str(res)))

            if res is None:
                return -1

            cameraId = res[0]
            (tm_year, tm_mon, tm_mday,
                tm_hour, tm_min, tm_sec, numMilliseconds) = res[1]

            #!!!!TODO: check CRC in state of BT iCam message - can be put in state
            """
            We check the validity of the date returned, since we had once
                errors in the BT message (either uSD card or radio error) - see
              Z:\1PhD\ReVival\Logs\NokiaN82\2013_11_17\stdout_2013_11_09_15_51_19.txt
                "GetInfoFromSMFBtMsg(): cameraId = 0, 
                    tm_year = 253733044, tm_mon = -1312688652, tm_mday = 1, 
                    tm_hour = 54, tm_min = -120, tm_sec=-120, 
                    numMilliseconds=736101691, btMsgStateTime = 1366848153 ."
            """
            if (tm_year > 1900 and tm_year < 2200) and \
                    (tm_mon > 0 and tm_mon <= 12) and \
                    (tm_mday > 0 and tm_mday <= 31) and \
                    (tm_hour >= 0 and tm_hour < 24) and \
                    (tm_min >= 0 and tm_min < 60) and \
                    (tm_sec >= 0 and tm_sec < 60) and \
                    (numMilliseconds >= 0 and numMilliseconds < 1000):
                pass
            else:
                return -1

            #def RemoveOldBluetoothMessages():
            """
            VERY IMPORTANT:
                We filter videos: the old ones w.r.t. the new ones already sent.
                  The video recentness info is kept as <deviceId, crtCameraId>
                        in btMsgMostRecentTime.
                We persist btMsgMostRecentTime on disk.
                The old videos are deleted.

                Note that only when the newly uploaded videos are uploaded to
                    YouTube, there is a pauseIntervalGdata pause to wait.
            """

            global btMsgMostRecentTime, btAddrTable

            btAddrTable[btClientDeviceId] = res[2]

            """
            # Unfortunately time.mktime() DOES NOT WORK ON
            #   PyS60 2.0. It simply crashes it without an
            #   exception...
            #From http://www.tutorialspoint.com/python/time_mktime.htm
            """
            if SYMBIAN_OS:
                crtVideoTime = (tm_year, tm_mon, tm_mday,
                                tm_hour, tm_min, tm_sec)
            else:
                crtVideoTime = time.mktime( (tm_year, tm_mon, tm_mday,
                                            tm_hour, tm_min, tm_sec,
                                            0, -1, -1) )

            updatedBtMsgMostRecentTime = False

            crtDevCam = (btClientDeviceId, cameraId)


            if crtDevCam not in btMsgMostRecentTime:
                btMsgMostRecentTime[crtDevCam] = crtVideoTime
                updatedBtMsgMostRecentTime = True
            else:
                DebugPrint("btMsgMostRecentTime[crtDevCam] = %s" % \
                            str(btMsgMostRecentTime[crtDevCam]))

                if btMsgMostRecentTime[crtDevCam] >= crtVideoTime:
                    #The crt video to upload to YT is older than the last uploaded.

                    DebugPrint("BluetoothMessageProcessAndDelete(): The " \
                            "current BT file (with crtVideoTime=%s) is " \
                            "older than the last processed one " \
                            "(with btMsgMostRecentTime[crtDevCam] = %s)." % \
                            (str(crtVideoTime), \
                                str(btMsgMostRecentTime[crtDevCam])))

                    BluetoothDeleteMessage(btMsgId)

                    return 0 # should it be -1? !!!!
                else:
                    btMsgMostRecentTime[crtDevCam] = crtVideoTime
                    updatedBtMsgMostRecentTime = True

            if (bluetoothMode == 1) and (updatedBtMsgMostRecentTime == True):
                # BT server and updated btMsgMostRecentTime
                StoreBtMsgMostRecentTime()

            aBatteryLevel = -1
            aChargerStatus = -1
            try:
                aBatteryLevel = res[3]
                aChargerStatus = res[4]
            except:
                DebugPrintErrorTrace()

            """
            IMPORTANT: GetInfoFromSMFBtMsg() needs to be called before to 
                update btMsgStateTime.
            BluetoothTimeSyncWithDrift() checks if BT message arrival time and
                btMsgStateTime are very different then send time-sync 
                adjustment command BT msg.
            Note that BluetoothTimeSyncWithDrift() is better to be called
                before BluetoothMessageProcessSMF(), which deletes the BT msg
                at the end.

            IMPORTANT NOTE: BluetoothMessageProcessSMF() deletes the BT msg
                at the end, by calling BluetoothDeleteMessage().
            """
            BluetoothTimeSyncWithDrift(btClientDeviceId, \
                                        sizeBluetoothMessage, btMsgId)

            BluetoothMessageProcessSMF(btMsgId, btMsgData, stateMediaData,
                                            fileNameBtMsg, btClientDeviceId,
                                            aBatteryLevel, aChargerStatus)
        else:
            """
            The message altough has iCam prefix name, does NOT have the
                right extension.
            """
            #if deleteMessageIfInvalid:
            BluetoothDeleteMessage(btMsgId, True)

            return -1

        return 0
    except:
        DebugPrintErrorTrace()
        return -1


btMsgList = None

"""
Returns the list of BT message filenames (WITHOUT path),
    in ~chronological order.
"""
def GetInboxFolderList():
    btInboxFolder = BLUETOOTH_INBOX_PATH
    """
    On Symbian^3 devices, the received BT messages are no longer stored in
        Messaging, but in the "E:\Received files" folder.
    So len(btMsgList) = 0.
    """

    btInboxFolderContent = os.listdir(btInboxFolder)

    """
    Sort BT messages in lexicographic order, which corresponds to
        chronological order
    """
    #btInboxFolderContent.sort(reverse=False)
    btInboxFolderContent.sort()

    # Sort BT messages in reverse lexicographic order, which corresponds to
    #btInboxFolderContent.sort(reverse=True)
    # sortedBTInboxFolderContent = sorted(btInboxFolderContent)
    """
    sort() without parameters is the ONLY one that works in
        Python 2.2. (Info on sort at
        http://wiki.python.org/moin/HowTo/Sorting/.)
    """
    # #btInboxFolderContent.sort()
    # #sortedBTInboxFolderContent = btInboxFolderContent

    # print "sortedMediaFolderContent =", sortedMediaFolderContent

    res = []

    # sortedBTInboxFolderContent
    for btFileName in btInboxFolderContent:
        #!!!!We should maybe discard the TXT and CMD files!!!! - they are sorted first after these!!!!
        if btFileName.startswith(BT_OBEX_FILENAME_PREFIX):
            # NOT NEEDED: pathFileName = btInboxFolder + "/" + btFileName

            """
            if SYMBIAN_OS:
                pathFileName = btInboxFolder + "\\" + btFileName
            else:
                pathFileName = btInboxFolder + "/" + btFileName
            """

            """
            EXTENSION_3GP = ".3gp"

            if os.path.isfile(pathFileName) and \
                #str.lower(pathFileName).endswith(EXTENSION_3GP):
                str.lower(pathFileName).endswith(
                    fileNameBtMsg[0: len(BT_OBEX_FILENAME_PREFIX)]):
            """

            res.append(btFileName)
            #res.append(pathFileName)

    return res


"""
Returns the list of BT messages, in ~chronological order.
"""
def GetBluetoothInboxMessageList():
    global btMsgList

    DebugPrint("Entered GetBluetoothInboxMessageList().")

    if ANDROID_OS:
        res = GetInboxFolderList()
    elif RASPBIAN_OS:
        res = GetInboxFolderList()
    elif WINDOWS_OS:
        res = GetInboxFolderList()
    elif SYMBIAN_OS:
        if SYMBIAN_3:
            res = GetInboxFolderList()
        else:
            """
            The BT message IDs are increasing with an increment of 2.
            Normally (but I have seen exceptions - see
              http://mobile-revival.110mb.com/ReVival/N95N95N95N95N95/log.html,
                on May 2nd, 2011 at 3:35AM),
                btMsgList has the elements sorted decreasingly following the
                msg ID.
            """

            """
            0x10009ED5 is the constant used in Symbian to specify only the
                Bluetooth messages from Inbox - we create a CInboxAdapter that
                reads only BT messages (it appears that the infrastructure is
                the same for SMS, MMS, etc).
            This value must not be changed.
            For message types see -
              http://discussion.forum.nokia.com/forum/showthread.php?17779-message-type-SMS-MMS-EMail..-etc .
            """
            res = bluetoothInbox.list_messages(0x10009ED5)

            if False:
                """
                !!!!Obtaining time from all messages, since we cannot really
                    rely on the reverse chronological order returned by
                    list_messages().
                """
                try:
                    btMsgList = res

                    DebugPrint("len(btMsgList) = %d" % len(btMsgList))

                    for btMsgId in btMsgList:
                        try:
                            """
                            description(btMsgId) returns the name of the file from the
                                BT message.
                            """

                            #!!!!TODO: make sure it's OK- Should be OK, since it's OK in BluetoothMessageProcessAndDelete()
                            fileNameBtMsg = GetBluetoothMessageName(btMsgId)

                            # Get message time of arrival.
                            btMsgArrivalTime = bluetoothInbox.time(btMsgId)

                            DebugPrint(
                                "GetBluetoothInboxMessageList(): " \
                                "btMsgId = %s, fileNameBtMsg = %s, " \
                                        "btMsgArrivalTime = %d" % \
                                        (str(btMsgId), fileNameBtMsg,
                                           btMsgArrivalTime))

                            try:
                                btMsgArrivalTimeStruct = \
                                    time.localtime(btMsgArrivalTime)

                                DebugPrint("    " + \
                                    time.strftime("%Y_%m_%d_%H_%M_%S", \
                                                btMsgArrivalTimeStruct))
                            except:
                                DebugPrintErrorTrace()
                        except:
                            DebugPrintErrorTrace()
                except:
                    DebugPrintErrorTrace()

    DebugPrint("GetBluetoothInboxMessageList(): len(res) = %d" % len(res))

    return res


def BluetoothMessageListProcess(deleteMessageIfInvalid=False, \
                                deleteAllBTMessages=False, \
                                processJustNonSMF_BtMsgs=False):
    global btMsgList, bluetoothInbox, conserveEnergy, \
                uploadHowManyOfLatestBluetoothMessages

    if conserveEnergy:
        if MY_DEBUG_STDOUT:
            print "BluetoothMessageListProcess(): bailing out because " \
                    "conserveEnergy = True."
            if conserveEnergy == False:
                sys.stdout.flush()
        return

    DebugPrint("Entered BluetoothMessageListProcess().")

    if ANDROID_OS or RASPBIAN_OS:
        try:
            btInboxFolderContent = GetInboxFolderList()

            if len(btInboxFolderContent) == 0:
                return

            # Process all the SMF BT messages.
            counterBluetoothMessages = 0

            if uploadHowManyOfLatestBluetoothMessages == -1:
                """
                This order ensures the messages are sent on the server exactly
                    in the order they were received via Bluetooth (which
                    should be chronological order)
                """
                rangeBtInboxFolderContent = range(len(btInboxFolderContent) - 1,
                                                                        -1, -1)
            elif uploadHowManyOfLatestBluetoothMessages > 0:
                lenFolder = len(btInboxFolderContent)
                howMany = min(lenFolder,
                                uploadHowManyOfLatestBluetoothMessages)

                """
                for index in range(howMany):
                    BluetoothMessageProcessAndDelete(btInboxFolderContent[index],
                                                        deleteMessageIfInvalid)
                """
                for index in range(howMany):
                    """
                    We send the most recent BT message received - and then
                        check for most recent again immediately afterwards.
                    """
                    BluetoothMessageProcessAndDelete(btInboxFolderContent[0],
                                                        deleteMessageIfInvalid)

                    """
                    We check if a new BT message has appeared - 
                        BluetoothMessageProcessAndDelete() can take long to
                        finish, especially because it uploads to Internet.
                    """
                    btInboxFolderContent = GetInboxFolderList()
                    if len(btInboxFolderContent) == 0:
                        """
                        We do this return because we access otherwise
                            btInboxFolderContent[0]
                        """
                        return
                return

                """
                if uploadHowManyOfLatestBluetoothMessages >= \
                    len(btInboxFolderContent):
                    rangeBtInboxFolderContent = \
                        range(len(btInboxFolderContent))
                else:
                    rangeBtInboxFolderContent = \
                        range(uploadHowManyOfLatestBluetoothMessages)
                """
            else:
                rangeBtInboxFolderContent = []

            for i in rangeBtInboxFolderContent:
                BluetoothMessageProcessAndDelete(btInboxFolderContent[i],
                        deleteMessageIfInvalid)

                if (counterBluetoothMessages %
                        NUM_UNSENT_PACKETS_BEFORE_DOWNLOAD_COMMANDS) == \
                        NUM_UNSENT_PACKETS_BEFORE_DOWNLOAD_COMMANDS - 1:
                    hasDownloadedNewCmd = DownloadCommands()
                counterBluetoothMessages += 1
        except:
            DebugPrintErrorTrace()

    elif SYMBIAN_OS or WINDOWS_OS:
        """
        if _PyS60_1_9_OR_NEWER:
            #The pyinbox module not supported in PyS60 1.9+
            return
        """
        try:
            btMsgList = GetBluetoothInboxMessageList()

            """
            # TODO!!!!Think if keep this - maybe should take it out or maybe put it in BluetoothMessageProcessAndDelete(doNotProcSMF=True)
            Process all CMD, TEXT and FILE (but not SMF - since they
                ~basically carry media) BT messages.
            Treat them in chronological order.
            """
            rangeBtMsgList = range(len(btMsgList) - 1, -1, -1)

            # Treat them in reverse chronological order (most recent ones first).
            for i in rangeBtMsgList:
            #for btMsgId in btMsgList:
                btMsgId = btMsgList[i]
                try:
                    """
                    description(btMsgId) returns the name of the file from the
                        BT message.
                    """
                    fileNameBtMsg = GetBluetoothMessageName(btMsgId)

                    # Checking if it is iCam BT message:
                    if fileNameBtMsg.startswith(BT_OBEX_FILENAME_PREFIX):
                        # Looking for iCam BT message that are not of SMF type.
                        if (NEW_BT_FORMAT and str.lower(fileNameBtMsg[-4:])
                                    not in BT_OBEX_EXTENSION_LIST_SMF) or \
                                (not NEW_BT_FORMAT and
                                fileNameBtMsg[len(BT_OBEX_FILENAME_PREFIX): ]. \
                                    startswith(BT_OBEX_FILENAME_PREFIX_TYPE_SMF)):
                            BluetoothMessageProcessAndDelete(btMsgId,
                                                        deleteMessageIfInvalid)
                except:
                    DebugPrintErrorTrace()

            if processJustNonSMF_BtMsgs == True:
                return
            #TODO!!!! END - see above

            """
            We delete all iCam BT messages received if
                deleteAllBTMessages == True.
              This means we erase the SMF BT messages without
                processing them.!!!!
            """
            if deleteAllBTMessages:
                """
                Once it crashed probably here, probably because len(btMsgList) ~= 1800
                Therefore, if btMsgList is too big, we make it smaller.
                """
                if len(btMsgList) > 300:
                    btMsgList = btMsgList[0: 300]

                for btMsgId in btMsgList:
                    try:
                        """
                        description(btMsgId) returns the name of the file
                            from the BT message.
                        """
                        fileNameBtMsg = GetBluetoothMessageName(btMsgId)

                        # sizeBluetoothMessage = bluetoothInbox.size(btMsgId)

                        # This is iCam BT message
                        if fileNameBtMsg.startswith(BT_OBEX_FILENAME_PREFIX):
                            # bluetoothInbox.delete(btMsgId)
                            BluetoothDeleteMessage(btMsgId)
                    except:
                        DebugPrintErrorTrace()

                        if MY_DEBUG_UPLOAD_MSG:
                            myText = "BluetoothMessageListProcess(): " \
                                    "Exception around " \
                                    "BluetoothDeleteMessage(%s)." % str(btMsgId)
                            UploadGZippedData(deviceId, myText,
                                    ICAM_SERVER_NAME,
                                    WEBPAGE_UL_GZIPPED_TEXT, None)
                return


            # Now we process the SMF BT messages.

            """
            In case we deleted messages in the previous lines, we read the
                updated bluetoothInbox.list_messages() .
            """
            btMsgList = GetBluetoothInboxMessageList()

            counterBluetoothMessages = 0
            if uploadHowManyOfLatestBluetoothMessages == -1:
                """
                This order ensures the messages are sent on the server exactly
                    in the order they were received via Bluetooth (which should
                    be chronological order).
                """
                rangeBtMsgList = range(len(btMsgList) - 1, -1, -1)

                for i in rangeBtMsgList:
                    BluetoothMessageProcessAndDelete(btMsgList[i],
                                                    deleteMessageIfInvalid)
                    if (counterBluetoothMessages %
                            NUM_UNSENT_PACKETS_BEFORE_DOWNLOAD_COMMANDS) == \
                            NUM_UNSENT_PACKETS_BEFORE_DOWNLOAD_COMMANDS - 1:
                        hasDownloadedNewCmd = DownloadCommands()

                    counterBluetoothMessages += 1
            elif uploadHowManyOfLatestBluetoothMessages > 0:
                for howMany in range(uploadHowManyOfLatestBluetoothMessages):
                    if len(btMsgList) == 0:
                        return

                    # We send the most recent BT message received
                    BluetoothMessageProcessAndDelete(btMsgList[0],
                                                        deleteMessageIfInvalid)

                    if (counterBluetoothMessages %
                            NUM_UNSENT_PACKETS_BEFORE_DOWNLOAD_COMMANDS) == \
                            NUM_UNSENT_PACKETS_BEFORE_DOWNLOAD_COMMANDS - 1:
                        hasDownloadedNewCmd = DownloadCommands()

                    counterBluetoothMessages += 1

                    #!!!!TODO: if we want more media upload fairness (among the BT clients) we should not update btMsgList
                    btMsgList = GetBluetoothInboxMessageList()

                    if len(btMsgList) == 0:
                        return
                return

                """
                if uploadHowManyOfLatestBluetoothMessages >= len(btMsgList):
                    rangeBtMsgList = range(len(btMsgList))
                else:
                    rangeBtMsgList = \
                        range(uploadHowManyOfLatestBluetoothMessages)
                """
            else:
                #rangeBtMsgList = []
                pass
        except:
            DebugPrintErrorTrace()


dictBtMsgTime = {}
#def BluetoothMessageProcessTIM(timeBluetoothMessage, btMsgId):
def BluetoothMessageProcessTIM(btMsgId):
    global dictBtMsgTime

    try:
        """
        DebugPrint("BluetoothMessageProcessTIM(timeBluetoothMessage=%d, " \
                    "btMsgId = %s" % (timeBluetoothMessage, str(btMsgId)))
        """

        # timeBluetoothMessage = bluetoothInbox.time(btMsgId)
        fileNameBtMsg = GetBluetoothMessageName(btMsgId)
        DebugPrint("BluetoothMessageProcessTIM(): " \
                    "fileNameBtMsg = %s." % fileNameBtMsg)
        """
        timeBluetoothMessage = bluetoothInbox.time(btMsgId)
        DebugPrint("BluetoothMessageProcessTIM(): " \
                    "fileNameBtMsg = %s, timeBluetoothMessage = %s." % \
                    (fileNameBtMsg, str(timeBluetoothMessage)))
        """

        """
         Apparently this crashes invariably iCam - sleep in a callback ain't
            good on Symbian:
        #SleepAndPetWatchdog(1.0)
        """

        #fileNameBtMsg = GetBluetoothMessageName(btMsgId)

        DebugPrint("BluetoothMessageProcessTIM(): fileNameBtMsg=%s" % \
                                                            fileNameBtMsg)

        if False:
            if not str.lower(fileNameBtMsg).endswith(BT_OBEX_EXTENSION_TIM):
                return

        if False:
            timeUnixEraSent = fileNameBtMsg[len(BT_OBEX_FILENAME_PREFIX) : ]
            timeUnixEraSent = timeUnixEraSent[ : -len(BT_OBEX_EXTENSION_TIM)]

            timeUnixEraSent = timeUnixEraSent[0 : timeUnixEraSent.rfind("_")]

            tokens = timeUnixEraSent.split("_")
            timeTuple = (int(tokens[0]), int(tokens[1]), int(tokens[2]),
                            int(tokens[3]), int(tokens[4]), int(tokens[5]),
                            int(tokens[6]))

            (tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec, \
                                numMilliseconds) = timeTuple

            timeSentUE = time.mktime( (tm_year, tm_mon, tm_mday,
                                            tm_hour, tm_min, tm_sec, -1, -1, -1) )

        timeUnixEraSent = fileNameBtMsg[len(BT_OBEX_FILENAME_PREFIX) : ]
        timeSentUE = int(timeUnixEraSent)
        """
        We have:
          - dictBtMsgTime[btMsgId] is the time the message was received on
                this phone.
          - timeSentUE is the time the message relays directly through its
                fileNameBtMsg.
        So instead of time timeSentUE we have dictBtMsgTime[btMsgId] and we
                need to adjust.
        """
        deltaTime = dictBtMsgTime[btMsgId] - timeSentUE

        #ExecuteCommands("set-date-and-time %d" % int(timeUnixEraSent))
        ExecuteCommands("set-date-and-time-delta %d" % int(deltaTime))
        deltaTime = dictBtMsgTime[btMsgId] - timeSentUE

        BluetoothDeleteMessage(btMsgId)
    except:
        DebugPrintErrorTrace()


def BluetoothMessageCallback(btMsgId):
    """
    It seems this function (BluetoothMessageCallback) is not called on
        Symbian^3 devices.
    """
    global bluetoothInbox, dictBtMsgTime

    crtTime = GetTime()

    # This is used in BluetoothMessageProcessTIM():
    dictBtMsgTime[btMsgId] = crtTime

    if SYMBIAN_OS:
        fileNameBtMsg = ""
        btMsgArrivalTime = 0

        """
        IMPORTANT: While in BluetoothMessageCallback(), we read INcorrect values
            for fileNameBtMsg (emtpy string) and btMsgArrivalTime ().
        """
        try:
            """
            description(btMsgId) returns the name/title of the BT
                message.

            Note that, without a good particular reason, the name is empty
                while in BluetoothMessageCallback().
            """
            fileNameBtMsg = GetBluetoothMessageName(btMsgId)

            """
            Get message time of arrival.

            In BluetoothMessageCallback(), the value is
                invariably 9.16120378515e+12 (it's float!) .
            Note that all messages have here the same value for
                btMsgArrivalTime, it seems, by looking at the logs:
                    btMsgArrivalTime = 9.16120378515e+12 .
            So btMsgArrivalTime ~= 9.16 billion. 

            This is wrong if we interpret it as number of seconds from
                the beginning of Unix era
                (in 2013 Oct - time.time() returns ~1.3 billion).

            So btMsgArrivalTime is not related to time.time().

            From C:\Symbian\Carbide\workspace\pyinbox\src\inboxadapter.cpp
              void CInboxAdapter::GetMessageTimeL(TMsvId aMessageId, TTime& aTime)
                  {
                  iMtm->SwitchCurrentEntryL(aMessageId);  
                  iMtm->LoadMessageL();

                aTime = (iMtm->Entry().Entry().iDate);
                  }

            From C:\Symbian\Carbide\workspace\pyinbox\src\module.cpp:
                /*
                 * Get message time of arrival.
                 */
                extern "C" PyObject *
                inb_time(INB_object* self, PyObject *args)
                {
                  TInt message_id = 0;

                  if (!PyArg_ParseTuple(args, "i", &message_id))
                    return NULL;
                  
                  TTime arrival;
                  
                  TRAPD(error, self->inbox->GetMessageTimeL((TMsvId)message_id, arrival));
                  
                  if (error != KErrNone)
                    return SPyErr_SetFromSymbianOSErr(error);

                  return SPyUnixTime_FromSymbianUniversalTime(arrival);
                }

                {"time", (PyCFunction)inb_time, METH_VARARGS},
            """
            btMsgArrivalTime = bluetoothInbox.time(btMsgId)

            """
            We CANNOT get the BT message name at this time so we don't do any
                processing.
            """
            #MyThreadStart(BluetoothMessageProcessTIM, (crtTime, btMsgId))
        except:
            DebugPrintErrorTrace()

        try:
            DebugPrint("BluetoothMessageCallback(btMsgId=%s): crtTime = %d, " \
                        "fileNameBtMsg = %s, " \
                        "btMsgArrivalTime = %s." % \
                        (str(btMsgId), crtTime, fileNameBtMsg,
                           str(btMsgArrivalTime)))

            if False:
                if MY_DEBUG_STDOUT:
                    """
                    The following instruction gives exception:
                        Traceback (most recent call last):
                          File "a.py", line 4147, in BluetoothMessageCallback
                        ValueError: timestamp out of range for platform time_t
                    """
                    btMsgArrivalTimeStruct = time.localtime(btMsgArrivalTime)
                    print "BluetoothMessageCallback():    " + \
                            time.strftime("%Y_%m_%d_%H_%M_%S", \
                                            btMsgArrivalTimeStruct)
        except:
            DebugPrintErrorTrace()


def BluetoothServerInitialize():
    global bluetoothInbox, btMsgList

    DebugPrint("Entered BluetoothServerInitialize().")

    if SYMBIAN_OS:
        try:
            bluetoothInbox = pyinbox.Inbox(0x10009ED5)
            bluetoothInbox.bind(BluetoothMessageCallback)
        except:
            DebugPrintErrorTrace()
        """
         bind(callable)
             Bind this instance to the device global inbox, callable function
             as parameter (with one argument, the id of the message received).
             Note that the new message received might not be of type SMS.
        """
        """
        We choose not to put "or accessPointRetryConnect" because this
            problem might be temporary - and get fixed in the following
            moments, while iCam is still running.
        """
        if accessPointName == u"":
            #appuifw.note(u"Warning: BT server not connected to the " \
            #   "Internet.")
            DebugPrint("Warning: BT server not connected to the Internet.")

        DebugPrint("Exiting BluetoothServerInitialize().")

        """
        try:
            #e32.ao_sleep(2)

            while True:
            #while numClients == 0:
                #time.sleep(1)
                #thread.start_new_thread(BluetoothMessageListProcess,
                #    (bluetoothFD, ))

                #bluetoothServerTimer.after(1,
                #              BluetoothMessageListProcessWrapper(bluetoothFD))

                BluetoothMessageListProcess()
                e32.ao_sleep(2)
        except:
            traceback.print_exc()
            sys.stderr.flush()
            sys.stdout.flush()
        """

        """
        Talk(conn, None)
        appuifw.note(u"Connection lost", "info")
        conn.close()
        """


OPP_SERVICE_NAME_TO_SEARCH_FOR = "Object Push"
def BluetoothClientDiscoverServer(aBluetoothServerAddress=""):
    global bluetoothServerAddress, bluetoothServerOPPServicePort

    """
    Here we basically discover if there is a device with BT address
        aBluetoothServerAddress and find out the port number
        for the service with the name OPP_SERVICE_NAME_TO_SEARCH_FOR.
      The port number bluetoothServerOPPServicePort is required by
        btsocket.bt_obex_send_file().
    """
    DebugPrint("Entered BluetoothClientDiscoverServer(" \
                "aBluetoothServerAddress = %s) - " \
                "bluetoothServerOPPServicePort = %s." % \
                (aBluetoothServerAddress, str(bluetoothServerOPPServicePort)))

    if SYMBIAN_OS:
        if aBluetoothServerAddress != "":
            bluetoothServerAddress = aBluetoothServerAddress

        """
        Note that the BT server can send BT msgs to more than 1 client,
            while the BT client can send BT msgs to only its server.
        """
        if ((bluetoothMode == 2) and # The phone is a BT client
                        (len(bluetoothServerOPPServicePort.items()) > 0)) or \
                    (bluetoothServerAddress in bluetoothServerOPPServicePort):
            DebugPrint("BluetoothClientDiscoverServer(): port already found " \
                        "--> bailing out")
            return

        try:
            conn = btsocket.socket(btsocket.AF_BT, btsocket.SOCK_STREAM)
            if bluetoothServerAddress == "":
                # bluetoothServerAddress, services = btsocket.bt_discover()
                (bluetoothServerAddress, bluetoothServerServices) = \
                    btsocket.bt_obex_discover()
            else:
                """
                We need to discover the services on our known target phone -
                    they can vary from instance to instance (i.e., service
                    "btchat" can be assigned to different ports in different
                    runs).
                """
                (bluetoothServerAddress, bluetoothServerServices) = \
                    btsocket.bt_obex_discover(bluetoothServerAddress)
                # bluetoothServerAddress, services = btsocket.bt_obex_discover()
            DebugPrint("BluetoothClientDiscoverServer(): " \
                        "bluetoothServerAddress = %s, " \
                        "bluetoothServerServices = %s." % \
                        (bluetoothServerAddress, bluetoothServerServices))

            #bluetoothServerOPPServicePort[bluetoothServerAddress] = -1

            for serviceName in bluetoothServerServices:
                if serviceName.find(OPP_SERVICE_NAME_TO_SEARCH_FOR) != -1:
                    """
                    The port number is usually 9 on Nokia Symbian phones.
                        But we cannot take this for granted.
                    """
                    bluetoothServerOPPServicePort[bluetoothServerAddress] = \
                                    bluetoothServerServices[serviceName]
        except:
            #bluetoothServerOPPServicePort = -1
            DebugPrintErrorTrace()

    DebugPrint("BluetoothClientDiscoverServer(): " \
        "bluetoothServerOPPServicePort = %s" % bluetoothServerOPPServicePort)

    DebugPrint("Exiting BluetoothClientDiscoverServer().")


def BluetoothClientInitializeInbox():
    global bluetoothInbox

    if SYMBIAN_OS:
        try:
            bluetoothInbox = pyinbox.Inbox(0x10009ED5)
            bluetoothInbox.bind(BluetoothMessageCallback)
        except:
            DebugPrintErrorTrace()


###############################################################################
###############################################################################
###############################################################################
###############################################################################
##########################End Bluetooth########################################
###############################################################################
###############################################################################
###############################################################################
###############################################################################
def SetUIOrientation(cameraId, isAfterImportCamera=True):
    if SYMBIAN_OS:
        global phoneModel
        global orientationForThisPhoneModel

        DebugPrint("Entered SetUIOrientation(cameraId = %d): " \
                    "orientationForThisPhoneModel = %s." % \
                    (cameraId, orientationForThisPhoneModel))

        try:
            """
            if isAfterImportCamera:
                # Release the camera so other applications may use it
                camera.release()
            """

            if cameraId == 0:
                """
                if orientationForThisPhoneModel == "landscape":
                    appuifw.app.orientation = "portrait"
                e32.ao_sleep(2)
                """
                appuifw.app.orientation = orientationForThisPhoneModel
            elif cameraId == 1:
                # appuifw.app.orientation = orientationForThisPhoneModel
                # """
                if SYMBIAN_3:
                    appuifw.app.orientation = orientationForThisPhoneModel
                else:
                    """
                    At least on S60 3rd edition, to view the viewfinder for
                        the VGA camera, we need to be in "portrait" mode.
                      In "landscape" mode we will see the image in the
                        viewfinder rotated.
                    """
                    appuifw.app.orientation = "portrait"
                # """

            """
            From [Mobile_Python_2007]:
                "The e32.ao_yield() at the end of the loop makes sure that the
                    system leaves some time to register the keyboard events,
                    as drawing in the tight loop consumes lots of CPU power
                    and might make the system unresponsive."
            From PyS60 2.0 documentation:
                Yields to the active scheduler to have ready active objects
                    with priority above normal scheduled for running. This
                    has the effect of flushing the eventual pending UI events.
                    Note that the UI callback code may be run in the context
                    of the thread that performs an ao_yield. For information
                    on active scheduler, see S60 SDK documentation [4].
            """
            e32.ao_yield()
            """
            if phoneModel in ["Nokia6120", "NokiaN95", "NokiaN82", "NokiaN8"]:
                e32.ao_sleep(3)
            """
            # e32.ao_sleep(3)

            """
            if isAfterImportCamera:
                # Reinitialize the camera to cope with the orientation change
                #camera._my_camera = camera._camera.Camera(0)
                camera.UseCamera(0)
            """
        except:
            DebugPrintErrorTrace()


def SetLocalPhotoResolution():
    global localPhotoResolution
    global localPhotoResolutionIndex, phoneModel, \
        cameraPhotoSizes_JPEG_Exif, cameraPhotoSizes_RGB24
    global numCamerasSupported

    if ANDROID_OS:
        localPhotoResolution = [(0, 0), (0, 0)]
    elif SYMBIAN_OS:
        """
        if localPhotoResolutionIndex[0] == 0:
            if (phoneModel == "NokiaN95") or (phoneModel == "NokiaN82"):
                # N95 and N82 report lowest resolution of (640,480), but
                #   actually require (320,240).
                localPhotoResolution = [ cameraPhotoSizes_JPEG_Exif[0][
                                    localPhotoResolutionIndex[0]], (320,240) ]

                # To be able to capture (2592, 1944) photo, I need to specify
                #   (1600, 1200) and use JPEG_Exif... Also, N95 and N82 report
                #   lowest resolution of (640,480), but actually require
                #   (320,240).
                #localPhotoResolution = [ (1600, 1200), (320, 240) ]
            elif phoneModel == "Nokia6680":
                localPhotoResolution = [ cameraPhotoSizes_JPEG_Exif[0][1],
                    cameraPhotoSizes_RGB24[1][localPhotoResolutionIndex[1]] ]

                if MODE_FOR_PHONE_WITH_LITTLE_RAM_AND_UNRELIABLE_MEM_CARD:
                    localPhotoResolution[0] = cameraPhotoSizes_JPEG_Exif[0][0]
            elif phoneModel == "Nokia6120":
                # For (1600, 1200) Main camera resolution it gives exception:
                #   "Size not supported for camera"
                localPhotoResolution = [ (1152, 864), cameraPhotoSizes_RGB24[1][ \
                                        localPhotoResolutionIndex[1]] ]
                # To be able to capture (1600, 1200) photo, I need to specify
                #   (1152, 864) and use JPEG_Exif...
            else:
                #localPhotoResolution = [ cameraPhotoSizes_JPEG_Exif[ \
                #   localPhotoResolutionIndex[0]], \
                #   cameraPhotoSizes_RGB24[localPhotoResolutionIndex[1]] ]
                localPhotoResolution = [ cameraPhotoSizes_JPEG_Exif[0][1],
                    cameraPhotoSizes_RGB24[1][localPhotoResolutionIndex[1]] ]
        else:
            # !!!!Implement well setting localPhotoResolution in case resolution
            #   specified by localPhotoResolutionIndex is not maximum
            #   supported, etc
            if (phoneModel == "NokiaN95") or (phoneModel == "NokiaN82"):
            # N95 and N82 report lowest resolution of (640,480), but actually
            #   require (320,240).
            #    localPhotoResolution = [ cameraPhotoSizes_JPEG_Exif[
            #       localPhotoResolutionIndex[0]], (320,240) ]
                # To be able to capture (2592, 1944) photo, I need to specify
                #   (1600, 1200) and use JPEG_Exif... Also, N95 and N82 report
                #   lowest resolution of (640,480), but actually require
                #   (320,240).
                localPhotoResolution = [ (1600, 1200), (320, 240) ]
            elif phoneModel == "Nokia6680":
                localPhotoResolution = [ cameraPhotoSizes_JPEG_Exif[0][1],
                    cameraPhotoSizes_RGB24[1][localPhotoResolutionIndex[1]] ]
                if MODE_FOR_PHONE_WITH_LITTLE_RAM_AND_UNRELIABLE_MEM_CARD:
                    localPhotoResolution[0] = cameraPhotoSizes_JPEG_Exif[0][0]
            elif phoneModel == "Nokia6120":
                # For (1600, 1200) Main camera resolution it gives exception:
                #   "Size not supported for camera"
                localPhotoResolution = [ (1152, 864),
                    cameraPhotoSizes_RGB24[1][localPhotoResolutionIndex[1]] ]

                # To be able to capture (1600, 1200) photo, I need to specify
                #   (1152, 864) and use JPEG_Exif...
            else:
                #localPhotoResolution = [ cameraPhotoSizes_JPEG_Exif[ \
                #   localPhotoResolutionIndex[0]], cameraPhotoSizes_RGB24[ \
                #   localPhotoResolutionIndex[1]] ]
                localPhotoResolution = [ cameraPhotoSizes_JPEG_Exif[0][1],
                    cameraPhotoSizes_RGB24[1][localPhotoResolutionIndex[1]] ]
        """

        try:
            if numCamerasSupported == 1:
                if phoneModel == "SamsungSGH-G810":
                    localPhotoResolution = \
                        [cameraPhotoSizes_RGB24[0][localPhotoResolutionIndex[0]],
                         (0, 0)]
                else:
                    localPhotoResolution = \
                        [cameraPhotoSizes_JPEG_Exif[0][ \
                        localPhotoResolutionIndex[0]],
                        (0, 0)]
            elif numCamerasSupported == 2:
                localPhotoResolution = \
                    [cameraPhotoSizes_JPEG_Exif[0][ \
                    localPhotoResolutionIndex[0]],
                    cameraPhotoSizes_RGB24[1][localPhotoResolutionIndex[1]]]
            if MODE_FOR_PHONE_WITH_LITTLE_RAM_AND_UNRELIABLE_MEM_CARD:
                localPhotoResolution[0] = \
                    cameraPhotoSizes_JPEG_Exif[0][0]

            DebugPrint("SetLocalPhotoResolution(): " \
                    "localPhotoResolution = %s" % str(localPhotoResolution))
        except:
            DebugPrintErrorTrace()
    elif SYMBIAN_UIQ_OS:
        localPhotoResolution = [(0, 0), (0, 0)]
    elif iOS_PYOBJC:
        localPhotoResolution = [(0, 0), (0, 0)]
    elif WINDOWS_OS:
        localPhotoResolution = [(0, 0), (0, 0)]
    elif WINDOWS_CE_OS_PYTHONCE:
        localPhotoResolution = [(0, 0), (0, 0)]


def Help():
    if SYMBIAN_OS:
        oldBody = appuifw.app.body
        try:
            myTextEditor = appuifw.Text()
            appuifw.app.body = myTextEditor

            # See http://pys60.garage.maemo.org/doc/s60/node21.html
            # It has only digit chars - letters are replaced by "square" char.
            # myTextEditor.font = (u"Nokia Hindi S60", 12, None)
            # "dense" # 'annotation','title','legend','symbol','dense','normal'
            myTextEditor.font = "normal"

            myTextEditor.add(u"Please visit http://go.to/slog/ to find out " \
                                "all info on how to use iCam.")
            myTextEditor.add(u"Copyright Alex Susu.")
            myTextEditor.add(u"\n")

            e32.ao_sleep(5.0)
        except:
            DebugPrintErrorTrace()

        appuifw.app.body = oldBody


def ConfirmQuit():
    if SYMBIAN_OS:
        try:
            myAnswer = appuifw.query(u"Are you sure you want to exit?",
                    "query")

            DebugPrint("ConfirmQuit(): myAnswer = %s" % str(myAnswer))

            # If press Cancel, myAnswer is None
            if myAnswer == True:
                Quit()
        except:
            DebugPrintErrorTrace()


"""
*args is for the Android callback
See http://docs.python.org/release/2.5.2/ref/function.html :
    "If the form ``*identifier'' is present, it is initialized to a tuple
        receiving any excess positional parameters, defaulting to the empty
        tuple. If the form ``**identifier'' is present, it is initialized to
        a new dictionary receiving any excess keyword arguments, defaulting to
        a new empty dictionary."
  See also http://stackoverflow.com/questions/400739/what-does-mean-in-python .
"""


def Quit(*args):
    global stdoutFile, stderrFile, deviceId
    global appLock


    #figleaf.stop()
    #figleaf.write_coverage('.figleaf')

    if ANDROID_OS:
        try:
            if MY_DEBUG_STDOUT:
                stdoutFile.close()
            if MY_DEBUG_STDERR:
                stderrFile.close()
        except:
            DebugPrintErrorTrace()

        """
        From http://docs.python.org/library/exceptions.html#exceptions.SystemExit
        "This exception is raised by the sys.exit() function. When it is not
            handled, the Python interpreter exits; no stack traceback is
            printed."
        "The os._exit() function can be used if it is absolutely positively
            necessary to exit immediately (for example, in the child process
            after a call to fork())"
        """
        # This doesn't work well because I'm catching exceptions in the caller
        #   of Quit(), namely EventHandler().
        #sys.exit()

        global reactiveLoopIsStarted
        reactiveLoopIsStarted = False
    elif SYMBIAN_OS:

        # See http://docs.python.org/library/os.html
        # os._exit(0)
        #myText = "Exiting iCam at %s - command given from the cellphone." \
        #   % GetCurrentDateTimeStringNice()
        myText = "Exiting iCam at %s." % GetCurrentDateTimeStringNice()

        if MY_DEBUG_UPLOAD_MSG:
            UploadGZippedData(deviceId, myText, ICAM_SERVER_NAME,
                              WEBPAGE_UL_GZIPPED_TEXT, None)

        DebugPrint(myText)
        #if MY_DEBUG_STDOUT:
        #    stderrFile.print(myText)
        #    sys.stderr.flush()

        try:
            # Close the viewfinder, if any open.
            camera.stop_finder()
            # Release the camera so that other programs can use it.
            camera.release()
        except:
            DebugPrint("Quit(): camera returned exception.")
            DebugPrintErrorTrace()

        # e32.ao_sleep(3)
        SleepAndPetWatchdog(3.0, False)

        try:
            global sleepAndPetWatchdogTimer
            sleepAndPetWatchdogTimer.cancel()
        except:
            # pass
            DebugPrint("Quit(): sleepAndPetWatchdogTimer.cancel returned " \
                        "exception.")
            DebugPrintErrorTrace()

        appLock.signal()

        # Set the UI orientation.
        appuifw.app.orientation = "automatic"  # "portrait"
        """
        This has the effect of flushing the eventual pending UI events.
        From [Mobile_Python_2007]: "The e32.ao_yield() at the end of the loop
            makes sure that the system leaves some time to register the
            keyboard events, as drawing in the tight loop consumes lots of CPU
            power and might make the system unresponsive."
        From PyS60 2.0 documentation: Yields to the active scheduler to have
            ready active objects with priority above normal scheduled for
            running. This has the effect of flushing the eventual pending UI
            events. Note that the UI callback code may be run in the context
            of the thread that performs an ao_yield. For information on active
            scheduler, see S60 SDK documentation [4].
        """
        e32.ao_yield()

        # Requests a graceful exit from the application as soon as the current
        #   script execution returns.
        appuifw.app.set_exit()
        """
        try:
            appuifw.app.set_exit()
        except:
            traceback.print_exc()
            sys.stdout.flush()
            sys.stderr.flush()
        """
        # sys.exit()

        try:
            if MY_DEBUG_STDOUT:
                """
                OLD: On Nokia 6680 we do not redirect the stdout to a file - it
                    would use too much space.
                if deviceId != IMEI_6680:
                    stdoutFile.close()
                """
                stdoutFile.close()

            if MY_DEBUG_STDERR:
                stderrFile.close()
        except:
            DebugPrintErrorTrace()

        #figleaf.stop()
        #figleaf.write_coverage('.figleaf')

    elif iOS_PYOBJC:
        try:
            if MY_DEBUG_STDOUT:
                stdoutFile.close()
            if MY_DEBUG_STDERR:
                stderrFile.close()
        except:
            DebugPrintErrorTrace()

        """
        From http://docs.python.org/library/exceptions.html#exceptions.SystemExit
            "This exception is raised by the sys.exit() function. When it is
            not handled, the Python interpreter exits; no stack traceback is
            printed."
        "The os._exit() function can be used if it is absolutely positively
            necessary to exit immediately (for example, in the child process
            after a call to fork())"

        # This doesn't work well because I'm catching exceptions in the caller
        #    of Quit(), namely EventHandler().
        """
        # sys.exit()

        # See http://docs.python.org/library/os.html
        os._exit(0)
    elif WINDOWS_CE_OS_PYTHONCE:
        try:
            if MY_DEBUG_STDOUT:
                stdoutFile.close()

            if MY_DEBUG_STDERR:
                stderrFile.close()
        except:
            DebugPrintErrorTrace()

        # See http://docs.python.org/library/os.html
        os._exit(0)



def PlayAudioFile(pathFileName):

    return


def StartGPS():
    global readGPS

    if SYMBIAN_OS:
        try:
            DebugPrint("StartGPS(): The available GPS modules: %s\n" % \
                                                str(positioning.modules()) + \
                        "StartGPS(): The default GPS module Id: %s.\n" % \
                                        str(positioning.default_module()) + \
                "StartGPS(): Detailed info about the default module: %s." % \
                    str(positioning.module_info(positioning.default_module())))

            """
            Select a module (in practice, selecting default module has no
                relevance.).
            """
            positioning.select_module(positioning.default_module())

            """
            Set requestors: at least one requestor must be set before
                requesting the current position or last position.
            The last requestor must always be service requestor (whether or not
                there are other requestors).
            """
            positioning.set_requestors([{"type": "service",
                    "format": "application", "data": "test_app"}])
        except:
            DebugPrintErrorTrace()

        try:
            readGPS = 1  # True
            print "StartGPS(): made readGPS = %d." % readGPS
            sys.stdout.flush()
            SetMenu()
            StoreState()
        except:
            DebugPrintErrorTrace()


def StopGPS():
    global readGPS

    try:
        readGPS = 0  # False
        DebugPrint("StopGPS(): readGPS = %d." % readGPS)
        SetMenu()
        StoreState()
    except:
        DebugPrintErrorTrace()


def StartBurstMode():
    global viewFinderSize, photoResolutionStr, burstModeIsStarted, prevVFFrame

    if ANDROID_OS:
        try:
            DebugPrint("StartBurstMode(): calling cameraStartPreview()")

            """
            Note: cameraStartPreview() starts the Viewfinder - at this moment
                the UI submenu is no longer available.
            """

            #!!!!TODO: On Samsung i5500 it gave exception: "com.googlecode.android_scripting.rpc.RpcError: Unknown RPC." (see Z:\1PhD\ReVival\Logs\Samsung_i5500\2014_01_01_2\stdout_2014_01_01_21_29_00.txt )
            myDroid.cameraStartPreview(0, 80) #20)
            """
            myDroid.cameraStartPreview(0, 80, LOCAL_FOLDER_MEDIA_FILES)

            myDroid.cameraStartPreview(resolutionLevel=0,
                    jpegQuality=20,  filepath=LOCAL_FOLDER_MEDIA_FILES)
            """


            """
            This generates events like
                Result(id=169, result=[{u'data': {u'format': u'jpeg', u'encoding': u'file', u'height': 288, u'width': 352, u'filename': u'/mnt/sdcard/external_sd/iCam/Media/prv-1287331584.jpg', u'quality': 80}, u'name': u'preview', u'time': 1377247976378000L}], error=None)
                Here, the image binary data is not included

            From http://www.mithril.com.au/android/doc/WebCamFacade.html:
                "It will generate "preview" events as images become
                    available."
                "If no file element is provided, the event will
                    include the image data as a base64 encoded string."

                Event details
                  - The data element of the preview event will be a map,
                    with the following elements defined.
                  - format- currently always "jpeg"
                  - width- image width (in pixels)
                  - height- image height (in pixels)
                  - quality- JPEG quality. Number from 1-100
                  - filename- Name of file where image was saved.
                    Only relevant if filepath defined.
                  - error- included if there was an IOException saving
                    file, ie, disk full or path write protected.
                  - encoding- Data encoding. If filepath defined, will
                    be "file" otherwise "base64"
                  - data- Base64 encoded image data.
            """

            #!!!!TODO: since cameraStartPreview() is not blocking (I think) we should not come back again here
            #SleepAndPetWatchdog(120.0, False)

            if False:
                """
                This does NOT generate events NOR saves JPEG files -
                    just streams MPEG over the very small HTTP server.
                """

                """
                webCamStart and webCamStop are used to start and stop
                    an Mpeg stream on a given port.
                webcamAdjustQuality is used to ajust the quality of the
                    streaming video. 
                webcamStart Starts an MJPEG stream and returns a Tuple
                    of address and port for the stream.
                resolutionLevel (Integer) increasing this number
                    provides higher resolution (default=0)
                jpegQuality (Integer) a number from 0-100 (default=20)
                port (Integer) If port is specified, the webcam service
                    will bind to port, otherwise it will pick any
                    available port. (default=0) 
                """

                #myDroid.webcamStart(resolutionLevel, jpegQuality, port)
                #myDroid.webcamStart(0, 40, 8080)
                myDroid.webcamStart(0, 60, 8080)

                #myDroid.webcamStop()

        except:
            DebugPrintErrorTrace()
        
    elif SYMBIAN_OS:
        try:
            DebugPrint("Entered StartBurstMode().")

            burstModeIsStarted = True

            # SetMenu()
            # StoreState()
            prevVFFrame = None

            # In Burst (Turbo) mode we don't resize, since it's too time consuming.
            viewFinderSize = photoResolutionStr[photoResolutionIndex][1]

            if (cameraMode[0] == 2) and (cameraMode[1] == 0):
                """
                It needs StartViewFinderForCamera("cameraId", isBackLightOn,
                    waitForVFFrames = False) because otherwise the VF is not
                    processing but 1-2 frames...
                """
                StartViewFinderForCamera(0, True, False)
            elif (cameraMode[0] == 0) and (cameraMode[1] == 2):
                """
                It needs StartViewFinderForCamera("cameraId", isBackLightOn,
                    waitForVFFrames = False) because otherwise the VF is not
                    processing but 1-2 frames...
                """
                StartViewFinderForCamera(1, True, False)
        except:
            DebugPrintErrorTrace()


def StopBurstMode():
    global viewFinderSize, VIEWFINDER_SIZE_ORIG, burstModeIsStarted

    DebugPrint("Entered StopBurstMode().")

    try:
        """
        camera.stop_finder()
        StopViewFinderForCameraCallable(False)
        """
        StopViewFinderForCameraCallable(True)

        viewFinderSize = VIEWFINDER_SIZE_ORIG
        burstModeIsStarted = False
    except:
        # SetMenu()
        # StoreState()
        DebugPrintErrorTrace()


if SYMBIAN_OS:
    """
    We define the Keyboard class so we can detect keypresses and react
        accordingly.
    """
    class Keyboard(object):

        def __init__(self, onevent=lambda : None):
            self._keyboard_state = {}
            self._downs = {}
            self._onevent = onevent

        def HandleEvent(self, myEvent):
            if myEvent["type"] == appuifw.EEventKeyDown:
                code = myEvent["scancode"]
                if not self.IsDown(code):
                    self._downs[code] = self._downs.get(code, 0) + 1
                self._keyboard_state[code] = 1
            elif myEvent["type"] == appuifw.EEventKeyUp:
                self._keyboard_state[myEvent["scancode"]] = 0
                # SetMenu()
                # StoreState()
            self._onevent()

        def IsDown(self, scancode):
            return self._keyboard_state.get(scancode, 0)

        def Pressed(self, scancode):
            if self._downs.get(scancode, 0):
                self._downs[scancode] -= 1
                return True
            return False


# viewFinderTimer = e32.Ao_timer()
# viewFinderTimerIsOn = False
startedReceivingFramesFromViewFinder = False
fillColorText = 0xFF0000
prevVFFrame = None
counterViewFinderCallback = 0

"""
On N82 (probably on N95 as well) if the viewfinder is started, but I give an
    ao_sleep() it appears the Main camera shutter gets closed even if the
    viewfinder is started.
"""
videoRecordFPS = 15

lastTimeAdapted = -1
numFramesLastTimeAdapted = 0
numFrames = 0

videoRecordStartTime = -1  # None
# videoRecordStartTime = GetTime()
videoRecordTime = 0

viewFinderStartTime = 0
avgFPS = 0.0

# posTimeForViewFinder should be adapted to viewFinderSize.
if SYMBIAN_OS:
    if SYMBIAN_3:
        posTimeForViewFinder = (118, 270)
    else:
        # posTimeForViewFinder = # (100, 230) #(200, 100)
        posTimeForViewFinder = (65, 190)

try:
    import RLearningController
    RLearningControllerIsImported = True


    class iCamMDP(RLearningController.MDP):
        """
        def __init__(self):
            pass
        """

        instantFPS = -1

        def SystemUpdate(self, actionScalar):
            global numThreadsUpload, photoQuality

            try:
                """
                !!!!
                Basically, crtState = actionScalar because our MDP state vars
                    are action vars also.
                """
                self.crtState = self.GetFactoredVectorFromFlatId(actionScalar)

                numThreadsUpload = 1  # 8
                # numThreadsUpload = self.crtState[0] + 1
                # photoQualityArray = [10, 25, 50, 75]
                photoQuality = photoQualityArray[self.crtState[1]]

                # For the moment, we have only 1 JPEG size.
                #photoSize = self.crtState[2]

                DebugPrint("SystemUpdate(actionScalar = %d): " \
                            "self.crtState = %s so have set " \
                            "numThreadsUpload = %d and photoQuality = %d." % \
                            (actionScalar, self.crtState,
                            numThreadsUpload, photoQuality))
            except:
                DebugPrintErrorTrace()

        def GetCurrentReward(self):
            return self.instantFPS


    """
    MDP state vars and actions:
        - num threads - 1..8
        - quality JPEG  - 10, 25, 50, 75% #, 90%
        - size JPEG - 1 (maybe 2 or 4).
        Note: the JPEG size is more difficult to adapt - either changing the VF
            resolution or resizing the image are difficult operations.

    Rewards:
        - FPS (or FPS * quality)

    Maybe I can use also:
        - time (at least speculate periodicity in traffic load, maybe also
            harvested energy: time of day, day of week).
        - energy
        - ...
    """
    photoQualityArray = [10, 40, 70, 100]
    mdp = iCamMDP(numStateVars=3, stateVarsCardinality=[1, 4, 1],
                  initState=[0, 1, 0])
    #stateVarsCardinality = [8, 4, 1] #[8, 4, 4]

    photoQuality = photoQualityArray[mdp.crtState[1]]

    rLearningCtrl = RLearningController.RLearningController(mdp)
    mdpEpoch = 0
except:
    RLearningControllerIsImported = False
    DebugPrint("Could not import RLearningController.")
    DebugPrintErrorTrace()


def Adapt(deltaTime, deltaNumFrames):
    global mdpEpoch

    try:
        # mdp.instantFPS = -1
        # Probably not good: mdp.SystemUpdate(actionScalar = 0)
        """
        mdp.crtState[0] = numThreadsUpload
        mdp.crtState[1] = photoQuality

        # For the moment, we have only 1 JPEG size.
        mdp.crtState[2] = 0
        """
        mdp.instantFPS = deltaNumFrames / deltaTime

        # Read mdp.instantFPS, numThreadsUpload, photoQuality

        mdpEpoch += 1

        DebugPrint("Adapt(deltaTime = %.2f, deltaNumFrames = %d): At " \
                    "epoch %d (time %s), mdp.instantFPS = %.3f " \
                    "(numFrames = %d)." % \
                    (
                        deltaTime, deltaNumFrames,
                        mdpEpoch, GetCurrentDateTimeStringWithMilliseconds(),
                        mdp.instantFPS, numFrames
                    ))

        # actionChosen = rLearningCtrl.ReactToSystemUpdate()
        rLearningCtrl.ReactToSystemUpdate(mdpEpoch)
    except:
        DebugPrintErrorTrace()


def ViewFinderCallbackS60Record(crtTime):
    global videoRecordStartTime, videoRecordTime
    global numFrames, videoRecordFPS
    global posTimeForViewFinder

    try:
        if (S60_EDITION[0] >= 3) and (videoRecordStartTime != -1) and \
                                        (numFrames % videoRecordFPS == 0):
            # appuifw.app.body.text((110, 270), u"%.2f" % videoRecordTime,
            #   font = "title", fill = 0)
            appuifw.app.body.text(posTimeForViewFinder, u"%02d:%02d" % \
                 (int(videoRecordTime) / 60, int(videoRecordTime) % 60), \
                    font="title", fill=0)
            # appuifw.app.body.text(posTimeForViewFinder, u"%02d" \
            #   % int(videoRecordTime), font = "title", fill = 0)

            """
            DebugPrint("ViewFinderCallback(): videoRecordTime = %f" % \
                        videoRecordTime)
            """

            videoRecordTime = crtTime - videoRecordStartTime

            appuifw.app.body.text(posTimeForViewFinder, u"%02d:%02d" % \
                 (int(videoRecordTime) / 60, int(videoRecordTime) % 60), \
                    font="title", fill=0xFF0000)
            """
            appuifw.app.body.text(posTimeForViewFinder,
                        u"%d" % int(videoRecordTime),
                        font = "title", fill = 0xFF0000)
            """
    except:
        DebugPrintErrorTrace()


def ViewFinderCallbackS60InfoAndLearning(crtTime):
    global numFrames
    global lastTimeAdapted, numFramesLastTimeAdapted
    global viewFinderStartTime, avgFPS
    global posTimeForViewFinder

    try:
        if lastTimeAdapted < 0:
            lastTimeAdapted = crtTime

        # and (cameraMode[0] == 2) and (numFrames > 0)
        """
        Note that S60 2nd edition has GetTime() with resolution of 1
            second (not milliseconds, as in S60 3rd ed).
        """
        if burstModeIsStarted:
            if RLearningControllerIsImported:
                if crtTime - lastTimeAdapted > 3.0:
                    """
                    We put this before Adapt() because Adapt() changes
                        mdp.instantFPS.
                    """
                    appuifw.app.body.text(posTimeForViewFinder,
                            u"FPS = %.2f (%.2f,%d%%)" % \
                            (mdp.instantFPS, avgFPS, photoQuality),
                            font="title", fill=0)

                    Adapt(crtTime - lastTimeAdapted, numFrames - \
                                            numFramesLastTimeAdapted)
                    numFramesLastTimeAdapted = numFrames
                    lastTimeAdapted = crtTime

                    # 0.1 to avoid Division by zero
                    avgFPS = numFrames / (GetTime() + 0.1 - viewFinderStartTime)

                    appuifw.app.body.text(posTimeForViewFinder,
                                            u"FPS = %.2f (%.2f,%d)" % \
                                            (mdp.instantFPS, avgFPS,
                                            photoQuality), font="title",
                                            fill=0xFF0000)
            else:
                appuifw.app.body.text(posTimeForViewFinder,
                                        u"FPS = %.2f" % avgFPS, font="title",
                                        fill=0)

                # 0.1 to avoid Division by zero
                avgFPS = numFrames / (GetTime() + 0.1 - viewFinderStartTime)

                appuifw.app.body.text(posTimeForViewFinder,
                                        u"FPS = %.2f" % avgFPS, font="title",
                                        fill=0xFF0000)
    except:
        DebugPrintErrorTrace()


def CallAlarmPhone():
    try:
        if ALARM_PHONE_NUMBER != "":
            DebugPrint("Motion detected --> calling " \
                        "at time %s." % time.asctime(GetCurrentDateTime()))

            telephone.dial(unicode(ALARM_PHONE_NUMBER))
            e32.ao_sleep(10)
            telephone.hang_up()
            # e32.ao_sleep(2)
            """
            !!!!After the call, the iCam Window is minimized, but the
                viewfinder is still active, even if not displayed.
                Maximize it using the appswitch module.

            # See http://discussion.forum.nokia.com/forum/showthread.php?214083-appswitch-module.
            #    Basically I should do:
            import appswitch
            appswitch.switch_to_fg(u"myapp")
            print appswitch.switch_to_bg(u"Menu")
            """
    except:
        DebugPrintErrorTrace()


def ViewFinderCallbackS60BurstMode(crtTime, crtVFFrame):
    #global phoneModel
    global viewFinderSize
    global prevVFFrame
    global LOCAL_FOLDER_MEDIA_FILES, counterViewFinderCallback, \
            storeLocallyMedia
    global MULTITHREADED_PHOTO_BURST_MODE_UPLOAD

    """
    DebugPrint("ViewFinderCallbackS60BurstMode(): prevVFFrame = %s, " \
                "crtVFFrame.size = %s, viewFinderSize = %s, " \
                "motionDetectionIsOn=%d." % \
                (str(prevVFFrame), str(crtVFFrame.size), str(viewFinderSize),
                    motionDetectionIsOn))
    """

    try:
        """
        ViewFinderCallbackS60BurstMode(): prevVFFrame = None,
            crtVFFrame.size = (320, 240), viewFinderSize = (320, 240),
            motionDetectionIsOn=1.
        """
        if crtVFFrame.size == viewFinderSize:

            if (cameraMode[0] == 2) and (cameraMode[1] == 0):
                cameraId = 0
            elif (cameraMode[0] == 0) and (cameraMode[1] == 2):
                cameraId = 1

            """
            if (motionDetectionIsOn == False) and \
                        (counterViewFinderCallback == 0):
            """
            if counterViewFinderCallback == 0:
                if (HTTP_INTERNET_UPLOAD_FAST == False) and \
                        (MULTITHREADED_PHOTO_BURST_MODE_UPLOAD == False) and \
                        (MULTITHREADED_PHOTO_BURST_MODE_UPLOAD2 == False) and \
                        (MULTITHREADED_PHOTO_BURST_MODE_UPLOAD3 == False):
                    hasDownloadedNewCmd = DownloadCommands()
                """
                if SYMBIAN_3:
                    appuifw.app.body.begin_redraw()
                    appuifw.app.body.blit(crtVFFrame)
                    appuifw.app.body.end_redraw()
                else:
                    appuifw.app.body.blit(crtVFFrame)
                """
                appuifw.app.body.blit(crtVFFrame)

            """
            DebugPrint("ViewFinderCallbackS60BurstMode(): prevVFFrame = %s, " \
                        "motionDetectionIsOn = %s" % \
                        (str(prevVFFrame), str(motionDetectionIsOn)))
            """

            motionDetected = False
            #if motionDetectionIsOn is True:
            if motionDetectionIsOn:
                if prevVFFrame is not None:
                    """
                    DebugPrint("ViewFinderCallbackS60BurstMode(): again " \
                                "prevVFFrame = %s, " \
                                "motionDetectionIsOn = %s" % \
                                (str(prevVFFrame), str(motionDetectionIsOn)))
                    """

                    if DetectMotion(prevVFFrame, crtVFFrame):
                        motionDetected = True
                        prevVFFrame = crtVFFrame
                        # Draw a red rectangle.

                        #crtVFFrame.rectangle([(5, 5), (35, 35)],
                        #   0xff0000)

                        #myCanvas = appuifw.Canvas()
                        #appuifw.app.body = myCanvas
                        global fillColorText
                        """
                        if SYMBIAN_3:
                            appuifw.app.body.begin_redraw()
                            appuifw.app.body.text((100, 175), u"MD",
                                font="title", fill=fillColorText)
                            appuifw.app.body.end_redraw()
                        else:
                            appuifw.app.body.text((100, 175), u"MD",
                                font="title", fill=fillColorText)
                        """
                        appuifw.app.body.text((100, 175), u"MD", \
                                            font="title", fill=fillColorText)
                        fillColorText -= 0x2000

                        #myCanvas.text((10, 175),
                        #   u"A: %04d %04d %04d" % (mov["data_1"],
                        #   mov["data_2"], mov["data_3"]),
                        #   font="title", fill = 0xff0000)

                        if False:
                            CallAlarmPhone()
                    else:
                        prevVFFrame = crtVFFrame
                        """
                        if SYMBIAN_3:
                            appuifw.app.body.begin_redraw()

                            # Erase text.
                            appuifw.app.body.text((100, 175), u"MD",
                                font="title", fill=0xFFFFFF)

                            appuifw.app.body.end_redraw()
                        else:
                            # Erase text.
                            appuifw.app.body.text((100, 175), u"MD",
                                font="title", fill=0xFFFFFF)
                        """
                        # Erase text.
                        appuifw.app.body.text((100, 175), u"MD", \
                                                font="title", fill=0xFFFFFF)
                else:
                    DebugPrint("ViewFinderCallbackS60BurstMode(): else branch " \
                                "prevVFFrame = %s, " \
                                "motionDetectionIsOn = %s" % \
                                (str(prevVFFrame), str(motionDetectionIsOn)))

                    prevVFFrame = crtVFFrame
                    # We consider the 1st frame to be relevant
                    motionDetected = True

            # No store, no upload
            if NoInternetConnection() and (storeLocallyMedia == 0) and \
                                                (bluetoothMode != 2):
                PetWatchdog()
            else:
                #pass
                if (motionDetectionIsOn == False) or \
                            (motionDetectionIsOn and motionDetected):
                    photoFileName = \
                        GetCurrentDateTimeStringWithMilliseconds() + \
                        "_%d.jpg" % cameraId
                    """
                    crtTime = GetCurrentDateTime()
                    #photoFileName = time.strftime("%Y_%m_%d_%H_%M_%S",
                    #   crtTime) + ("_%d.png" % cameraId)

                    crtTime2 = GetTime()

                    #See http://discussion.forum.nokia.com/forum/showthread.php?116978-What-is-the-time-granularity-in-Pys60 .
                    numMilliseconds = (crtTime2 - int(crtTime2)) * 1000
                    photoFileName = time.strftime("%Y_%m_%d_%H_%M_%S",
                        crtTime) + ("_%03d_%d.jpg" % (numMilliseconds,
                        cameraId))
                    """

                    # if storeLocallyMedia == 0:
                    """
                    We check
                        MULTITHREADED_PHOTO_BURST_MODE_UPLOAD == False
                        because if
                        MULTITHREADED_PHOTO_BURST_MODE_UPLOAD == True,
                        then we upload photoPathFileName but since
                        there can be several threads simultaneously
                        uploading they have to save to different files.
                    """
                    if (storeLocallyMedia == 0) and \
                            (MULTITHREADED_PHOTO_BURST_MODE_UPLOAD == False):
                        # Since we don't want to store the file, we
                        #   save it in the RAM drive.
                        #photoPathFileName = "D:/iCamTemp.jpg"
                        photoPathFileName = LOCAL_FOLDER_TEMP + \
                                            "/iCamTemp.jpg"
                    else:
                        photoPathFileName = \
                            LOCAL_FOLDER_MEDIA_FILES + "/" + \
                            photoFileName

                    # pic = graphics.Image.open(photoPathFileName)
                    """
                    Requires backslashes, otherwise pic.save gives
                        exception:
                        "SymbianError: [Errno -28] KErrBadName"
                    """
                    photoPathFileNameWithBackslashes = \
                                photoPathFileName.replace("/", "\\")

                    """
                    From PyS60 2.0 manual:
                        save(filename[,callback=None, format=None,
                            quality=75, bpp=24, compression='default'])
                    Saves the image into the given file. The supported
                        formats are JPEG and PNG. If format is not
                        given or is set to None, the format is
                        determined based on the file name extension:
                        '.jpg' or '.jpeg' are interpreted to be in JPEG
                        format and '.png' to be in PNG format.
                        filename should be a full path name.
                    When saving in JPEG format, the quality argument
                        specifies the quality to be used and can range
                        from 1 to 100.
                    When saving in PNG format, the bpp argument
                        specifies how many bits per pixel the resulting
                        file should have, and compression specifies the
                        compression level to be used.
                    Valid values for bpp are:
                        1: Black and white, 1 bit per pixel
                        8: 256 gray shades, 8 bits per pixel
                        24: 16.7 million colors, 24 bits per pixel
                    Valid values for compression are:
                        'best': The highest possible compression ratio,
                            the slowest speed
                        'fast': The fastest possible saving, moderate
                            compression
                        'no': No compression, very large file size
                        'default': Default compression, a compromise
                            between file size and speed

                    If callback is given,the operation is asynchronous.
                        When the saving is complete, the callback is
                        called with the result code.
                    """

                    """
                    For Burst (Turbo) mode we do not resize - we just
                        save the photos in JPEG instead of PNG
                        (JPEG compresses fast and a lot when compared
                        to PNG).

                    Save the photo (as JPEG) with 75%?? quality
                        locally.
                    """

                    crtVFFrame.save(photoPathFileNameWithBackslashes,
                                    None, None, quality=photoQuality)

                    DebugPrint("ViewFinderCallback(): Saved %s at " \
                                "photoQuality = %d (free_ram = %d)." % \
                                (photoPathFileName, photoQuality,
                                    GetFreeRAM()))
                    """
                    DebugPrint("ViewFinderCallback(): Saved %s " \
                                "(free_ram = %d, GetTime() = %.3f," \
                                " time.clock() = %.3f)." % \
                                (photoPathFileName, GetFreeRAM(),
                                    GetTime(), time.clock()))
                    """

                    def UploadThread2(photoFileName, photoPathFileName):
                        """
                        http://stackoverflow.com/questions/2576534/does-pythons-httplib-httpconnection-block
                        "Although you can do asynchronous requests, you
                            will have to make you entire program
                            async-friendly. Async does not magically
                            make your code non-blocking. It would be
                            much easier to do the request in another
                            thread or process if you don't want to
                            block your main loop."
                        """

                        """
                        We do not sleep to give time to the phone to
                            complete saving the video file, since the
                            save() returns when the file is really
                            saved.
                        """
                        # e32.ao_sleep(2)
                        UploadStateAndFileAndStoreState(
                            deviceId, cameraId,
                            photoFileName, photoPathFileName,
                            ICAM_SERVER_NAME,
                            WEBPAGE_UL_GZIPPED_STATE_AND_FILE
                            )

                    if MULTITHREADED_PHOTO_BURST_MODE_UPLOAD:
                        if uploadMediaToPicasa or (useiCamServer == 2): # (useiCamServer > 0)
                            """
                            Multithreaded version --> it tries to
                                paralelize as much as possible in
                                order to send as many photos as
                                possible (max the fps) to the server.
                            """
                            """
                            thread.start_new_thread(UploadThread2,
                                    (photoFileName, photoPathFileName))
                            """
                            MyThreadStart(UploadThread2, \
                                            (photoFileName, photoPathFileName))
                    else:
                        if uploadMediaToPicasa or (useiCamServer == 2): # (useiCamServer > 0)
                            # Single-threaded version
                            res = UploadStateAndFileAndStoreState(
                                    deviceId, cameraId,
                                    photoFileName, photoPathFileName,
                                    ICAM_SERVER_NAME,
                                    WEBPAGE_UL_GZIPPED_STATE_AND_FILE)

                        """
                        ERASE_ORIGINAL_MEDIA_FILE_AFTER_READ == True
                            --> UploadStateAndFileAndStoreState()
                                gives os.unlink(photoPathFileName)
                        """
                        if (storeLocallyMedia == 0) and \
                            (ERASE_ORIGINAL_MEDIA_FILE_AFTER_READ == False):
                            """
                            print "Since storeLocallyMedia == 0, " \
                                    "I saved the photo on D: and " \
                                    "now I erase it."
                            """
                            # sys.stdout.flush()
                            try:
                                if SYMBIAN_OS:
                                    #if pyS60VersionNumber > 14:
                                    if _PyS60_1_9_OR_NEWER:
                                        mediaUploadedLock.wait()

                                os.unlink(photoPathFileName)
                            except:
                                DebugPrintErrorTrace()

            if counterViewFinderCallback == \
                                NUM_FRAMES_TO_UPDATE_VIEWFINDER_EVERY - 1:
                counterViewFinderCallback = 0
            else:
                counterViewFinderCallback += 1
        else:
            DebugPrint("ViewFinderCallbackS60BurstMode(): not saving since " \
                        "crtVFFrame.size = %s." % str(crtVFFrame.size))
    except:
        DebugPrintErrorTrace()


def ViewFinderCallback(crtVFFrame):
    global viewFinderSize
    global posTimeForViewFinder
    global numFrames

    if SYMBIAN_OS:
        try:
            """
            We try to avoid the camera shutter closes on models like N95, N82
                - this might happen for example if the Burst (Turbo) and Motion
                Detection modes are on and there is no motion.
            """
            e32.reset_inactivity()

            """
            See http://kernel.symbian.org/wiki/Apps:Python_on_Symbian/07._Graphics_and_Multimedia
                (from "I had to use begin_redraw / end_redraw in almost all
                examples of this book that uses canvas to make them work
                correctly.")

            Less important:
                http://www.symbian-freak.com/forum/viewtopic.php?f=32&t=41207&start=0 .

            http://pys60.garage.maemo.org/doc/s60/node25.html#1342
                (PyS60 2.0 doc??)

            Question: Why is non-redraw drawing bad for performance?
                * The window server caches drawing operations in the redraw
                    store. Delimiting drawing with begin_redraw()/end_redraw()
                    allows window server to efficiently manage drawing
                    operations.
              If applications perform drawing operations outside
                begin_redraw/end_redraw, window server cannot cull drawing
                operations from its cache of drawing operations, because it
                cannot know whether a set of drawing operations has been
                superceded by a new set. In this scenario every frame of
                drawing that is done on a non-redraw drawing window will become
                slower and slower as it draws all the drawing operations for
                the entire history of the window (well actually up until the
                last begin_redraw/end_redraw for the whole window).
              If an application performs begin_redraw/end_redraw, it tells the
                window server that it can throw away any old drawing operations
                it had for the area of the window specified in the redraw, thus
                allowing for more optimal management of drawing operations.

            Question: What are the changes required for redraw drawing?
                * Applications should delimit their drawing with
                    begin_redraw()/end_redraw() - i.e. they should replace
                    non-redraw drawing with redraw drawing. Sometimes, this is
                    as straight forward as adding these calls to existing
                    rendering code. In other cases (where the application has
                    been drawing using "incremental updates" to the window, the
                    application drawing code would need to be reworked to
                    perform a full refresh of the area redrawn for the rect
                    provided in begin_redraw(rect).
            """
            if SYMBIAN_3:
                appuifw.app.body.begin_redraw()

            global startedReceivingFramesFromViewFinder
            startedReceivingFramesFromViewFinder = True
            """
            global viewFinderTimer, viewFinderTimerIsOn
            if viewFinderTimerIsOn:
                #viewFinderTimerIsOn = False
                viewFinderTimer.cancel()
            """

            # """
            # newImage = graphics.Image.new(crtVFFrame.size, '1')
            # newImage = graphics.Image.new(crtVFFrame.size)

            """
            # See
            #   http://discussion.forum.nokia.com/forum/showthread.php?154420-How-to-get-pixel-colour-from-a-graphics-object :
            for y in range(height):
                for x in range(width):
            """

            """
            width, height = crtVFFrame.size
            for y in range(height / 2 - 10, height / 2 + 10):
                for x in range(width / 2 - 20, width / 2 + 20):
                    r, g, b = crtVFFrame.getpixel( (x, y) )[0]
                    newRGB = (255 - r, 255 - g, 255 - b)
                    # From
                    #   http://discussion.forum.nokia.com/forum/showthread.php?114751-Is-there-a-setpixel-for-image-objects
                    #   (a bit also
                    #   http://discussion.forum.nokia.com/forum/showthread.php?120004-How-to-access-pixel-array-of-image-captured-by-viewfinder)
                    #newImage.point( (x, y), newRGB )
                    #newImage.point( (x, y), newRGB )
                    crtVFFrame.point( (x, y), newRGB )
            """

            crtTime = GetTime()

            ViewFinderCallbackS60Record(crtTime)

            ViewFinderCallbackS60InfoAndLearning(crtTime)

            """
            DebugPrint("ViewFinderCallback(): numFrames = %d." % (numFrames))
            """
            numFrames += 1

            if burstModeIsStarted and ((cameraMode[0] == 2) or \
                                        (cameraMode[1] == 2)):
                ViewFinderCallbackS60BurstMode(crtTime, crtVFFrame)
            else:
                """
                if SYMBIAN_3:
                    appuifw.app.body.begin_redraw()
                    appuifw.app.body.blit(crtVFFrame)
                    appuifw.app.body.end_redraw()
                else:
                    appuifw.app.body.blit(crtVFFrame)
                """

                """
                DebugPrint("ViewFinderCallback(): crtVFFrame.size = %s, " \
                            "viewFinderSize = %s" % \
                            (str(crtVFFrame.size), str(viewFinderSize)))
                """

                appuifw.app.body.blit(crtVFFrame)

            # """
            #print crtVFFrame.getpixel( (0, 0) )
            #sys.stdout.flush()
            #appuifw.app.body.blit(newImage)

            # This displays the viewfinder image:
            #appuifw.app.body.blit(crtVFFrame)
            if SYMBIAN_3:
                appuifw.app.body.end_redraw()
        except:
            DebugPrintErrorTrace()


viewFinderStarted = False

def StopViewFinderForCameraCallable(reallyStopViewFinder=True):
    global viewFinderStarted
    global doNotDisplayRedrawInfo

    DebugPrint("Entered StopViewFinderForCameraCallable().")

    if SYMBIAN_OS:
        """
        This check is here to prevent the user to hit "Stop Viewfinder" when
            the phone records. I think this is a bit paranoid - I guess this
            case is not easy to reach.
        """
        if videoRecordStartTime != -1:
            DebugPrint("StopViewFinderForCameraCallable(): " \
                        "videoRecordStartTime = %d, which means that it is " \
                        "video recording --> bailing out." % \
                                            videoRecordStartTime)
            return

        viewFinderStarted = False
        SetMenu()

        if reallyStopViewFinder:
            try:
                camera.stop_finder()
                """
                This means we just stopped the VGA camera viewfinder - this happens
                    for the S60 3rd edition phones, for ex.
                """
                if appuifw.app.orientation == "portrait":
                    SetUIOrientation(0, True)
                doNotDisplayRedrawInfo = False
            except:
                DebugPrintErrorTrace()


def StartViewFinderForCamera(cameraId, isBackLightOn, waitForVFFrames=False):
    global deviceId, localPhotoResolution, viewFinderSize
    global viewFinderStarted
    global doNotDisplayRedrawInfo

    if SYMBIAN_OS:
        DebugPrint("Entered StartViewFinderForCamera(cameraId = %d, " \
                    "isBackLightOn = %d) at %s: viewFinderSize = %s." % \
                    (cameraId, isBackLightOn,
                       GetCurrentDateTimeStringNice(), str(viewFinderSize)))

        if videoRecordStartTime != -1:
            DebugPrint("StartViewFinderForCamera(): " \
                        "videoRecordStartTime = %d, which means that it is " \
                        "video recording --> bailing out." % \
                        videoRecordStartTime)
            return

        viewFinderStarted = True
        SetMenu()

        try:
            camera.stop_finder()

            """
            Inspired from
                http://discussion.forum.nokia.com/forum/showthread.php?164123-n73-fullscreen-camera-odd-behaviour .
            """
            # camera.release()

            SetUIOrientation(cameraId, True)

            if phoneModel != "SamsungSGH-G810":
                """
                Inspired from
                    http://discussion.forum.nokia.com/forum/showthread.php?164123-n73-fullscreen-camera-odd-behaviour .
                Reinitialize the camera to cope with the orientation change.
                # camera._my_camera = camera._camera.Camera(cameraId)
                # This is for camera2: camera.UseCamera(cameraId)
                """
                GeneralUseCameraS60(cameraId)

            # Erase the screen to only show the view finder on the screen.
            ClearScreen()
        except:
            (exceptionType, exceptionValue, exceptionTraceback) = \
                                                        sys.exc_info()
            myText = "Exception in StartViewFinderForCamera(%d) - " \
                        "details: photoMode = %s, free_ram = %d. %s." % \
                        (cameraId, photoModeStr[photoModeIndex[cameraId]][1],
                            GetFreeRAM(),
                            repr(traceback.format_tb(exceptionTraceback)))

            """
            myText = "StartViewFinderForCamera(%d) returned an exception. " \
                        "Bailing out..." % cameraId
            """
            DebugPrint(myText)
            DebugPrintErrorTrace()

            if MY_DEBUG_UPLOAD_MSG:
                UploadGZippedData(deviceId, myText, ICAM_SERVER_NAME,
                                  WEBPAGE_UL_GZIPPED_TEXT, None)

        try:
            DebugPrint("StartViewFinderForCamera(): calling " \
                        "camera.start_finder().")

            """
            NOTE: For both cameraId 0 and 1, camera.start_finder can trigger a
                call to RedrawHandler(), so in this case we make
                doNotDisplayRedrawInfo = True and make it False only in
                StopViewFinderForCameraCallable.
            """
            doNotDisplayRedrawInfo = True

            """
                   start_finder(callable[,          backlight_on=1,
                                                        size=main pane size ])
            """
            camera.start_finder(ViewFinderCallback, isBackLightOn,
                                                        viewFinderSize)

            global numFrames, viewFinderStartTime

            numFrames = 0
            if burstModeIsStarted and (cameraMode[0] == 2):
                viewFinderStartTime = GetTime()

            if waitForVFFrames:
                """
                On N95 and N82 it takes 3-5 seconds for the ViewFinder to turn
                    on. We want to wait exactly what is needed to start the VF.
                #global viewFinderTimer, viewFinderTimerIsOn
                #viewFinderTimerIsOn = True

                # It sometimes breaks the control flow - the app is no longer
                #   progressing.
                #viewFinderTimer.after(15)
                """
                # """
                global startedReceivingFramesFromViewFinder

                startedReceivingFramesFromViewFinder = False

                while startedReceivingFramesFromViewFinder == False:
                    # pass
                    """
                    It crashes sometimes the app, especially if doing this in
                        Burst (Turbo) mode.
                    """
                    # e32.ao_sleep(1)
                    """
                    It implements brute busy waiting, which does not allow to
                        execute even callbacks, etc. (To stop a long
                        time.sleep() I had to remove the mem card...).
                        To make it work I use ao_yield().
                    """
                    time.sleep(0.5)
                    """
                    Inspired from
                        http://developer.symbian.org/wiki/Apps:Python_on_Symbian/03._System_Information_and_Operations#Wait.2FSleep .
                    """
                    e32.ao_yield()
                    """
                    But even so, for Burst (Turbo) mode, only 1-2 frames are
                        received in ViewFinderCallback(), and nothing else.
                        - is it missing some important active tasks?
                    """
                # """
            DebugPrint("StartViewFinderForCamera(): Finished " \
                        "StartViewFinderForCamera() at %s." % \
                        GetCurrentDateTimeStringNice())
        except:
            (exceptionType, exceptionValue, exceptionTraceback) = \
                sys.exc_info()

            myText = "Exception in StartViewFinderForCamera(%d) - details: " \
                        "photoMode = %s, free_ram = %d. %s." % \
                        (cameraId, photoModeStr[photoModeIndex[cameraId]][1],
                           GetFreeRAM(),
                   repr(traceback.format_tb(exceptionTraceback)))

            """
            myText = "StartViewFinderForCamera(%d) returned an exception. " \
                        "Bailing out..." % cameraId
            """
            DebugPrint(myText)
            DebugPrintErrorTrace()

            if MY_DEBUG_UPLOAD_MSG:
                UploadGZippedData(deviceId, myText, ICAM_SERVER_NAME,
                                  WEBPAGE_UL_GZIPPED_TEXT, None)


StartViewFinderForCameraCallable = lambda cameraId, isBackLightOn: \
    lambda : StartViewFinderForCamera(cameraId, isBackLightOn, False)


"""
These functions set the capture time interval, the number of cameras used,
    resolution, zoom and photo (JPEG) quality to the selected value.
"""
def SetPauseInterval_real(aPauseInterval):
    global pauseIntervalStr, pauseInterval, MENU_SELECT_PREFIX
    global reactiveLoopIsStarted, burstModeIsStarted

    DebugPrint("SetPauseInterval_real(): pauseInterval = %d, " \
                "aPauseInterval = %d, len(pauseIntervalStr) = %d." % \
                (pauseInterval, aPauseInterval, len(pauseIntervalStr)))

    pauseInterval = aPauseInterval
    # SetMenu()
    StoreState()

    """
    # This might be the reason my app crashed.
    UploadText("New photo interval: %d." % pauseInterval,
                ICAM_SERVER_NAME, WEBPAGE_UL_GZIPPED_TEXT)
    """
    if WINDOWS_CE_OS_PYTHONCE:
        StoreConfig_CamAutoCfg_WinCE()

    if ANDROID_OS:
        #!!!!TODO: implement properly for Android
        pass
    elif SYMBIAN_OS:
        if pauseInterval == 0:
            if (reactiveLoopIsStarted == True) and \
                            (cameraMode[0] * cameraMode[1] == 0) and \
                            (cameraMode[0] + cameraMode[1]) > 0:
                SetRecordDuration(0, 0)
                SetRecordDuration(1, 0)
                SetRecordDuration(2, 0)
                StartBurstMode()
        else:
            if burstModeIsStarted == True:
            # if reactiveLoopIsStarted == True:
                StopBurstMode()
                ReactiveLoop()


"""
We define this function as 2 lambdas to make it an callable even if it is
    passed the aPauseInterval parameter.
"""
SetPauseInterval = lambda aPauseInterval: lambda : \
    SetPauseInterval_real(aPauseInterval)


# This var is required as global.
pauseFormSaved = False


# Uses a Form
def PauseIntervalMenu(*args):
    global pauseFormSaved
    global pauseInterval

    DebugPrint("PauseIntervalMenu(): pauseInterval = %d, " \
                "len(pauseIntervalStr) = %d." % \
                (pauseInterval, len(pauseIntervalStr)))

    if ANDROID_OS:
        try:
            """
            res = int(DialogGetInput("Enter Pause Interval:", "[seconds]",
                                        str(pauseInterval)))
            """
            #res = DialogGetInput("Enter Pause Interval:", "[seconds]",
            res = DialogGetInput(
                        "Enter Pause Interval:", "[seconds (0 = Burst mode)]",
                        str(pauseInterval))

            DisplayNote("res = %s" % str(res), waitTime=1.0)
            res = int(res)
            SetPauseInterval(res)()
        except:
            # myDroid.makeToast("pauseInterval = %d." % str(pauseInterval))
            DebugPrintErrorTrace()
    elif SYMBIAN_OS:
        """
        From http://wiki.forum.nokia.com/index.php/How_to_use_Form_in_Python_for_S60
                (and also http://www.mobilenin.com/pys60/info_tabs_forms.htm):
        """

        """
        try:
            appuifw.app.title = u"Pause Interval"

            selectionListPI = []
            for i in range(len(pauseIntervalStr)):
                if pauseIntervalStr[i][1] == pauseInterval:
                    selectionListPI += [unicode(MENU_SELECT_PREFIX
                            + pauseIntervalStr[i][0])]
                else:
                    selectionListPI += \
                        [unicode(pauseIntervalStr[i][0])]

            # As the UX team noticed, on touch devices, to select you need to
            #    double tap (1 tap for focus, another for select).
            index = appuifw.selection_list(selectionListPI, 1)
            if index is not None:
                # pauseInterval = pauseIntervalStr[index][1]
                SetPauseInterval(pauseIntervalStr[index][1])()
        except:
            DebugPrintErrorTrace()
        """

        try:
            # Initialize a boolean variable to know whether the form is saved
            pauseFormSaved = False

            # Create a list to be used in "combo" selection mode.
            piComboList = []
            for piStr in pauseIntervalStr:
                piComboList += [u"%s" % piStr[0]]

            piIndex = 0
            for i in range(len(pauseIntervalStr)):
                if pauseIntervalStr[i][1] == pauseInterval:
                    piIndex = i

            myFields = [(u"Pause Interval", "combo", (piComboList,
                        piIndex))]  # (u"", "text", u"Pause Interval"),

            appuifw.app.title = u"Pause Interval"

            # Creates the form.
            """
            pauseForm = appuifw.Form(myFields,
                flags=appuifw.FFormEditModeOnly)
            """
            pauseForm = appuifw.Form(myFields,
                                        flags=appuifw.FFormEditModeOnly |
                                        appuifw.FFormDoubleSpaced)

            # pauseForm = appuifw.Form(myFields, appuifw.FFormEditModeOnly)

            # Define a function to be called when the form is saved.
            def PauseFormSaved(arg):
                global pauseFormSaved
                pauseFormSaved = True
                return True

            # Assign the save function
            pauseForm.save_hook = PauseFormSaved

            # Show the form.This operation is blocking until we close the form.
            pauseForm.execute()

            # After the form is saved and closed, display the information.
            if pauseFormSaved == True:
                #print pauseForm[0][2]
                #appuifw.note(unicode("Google Username: " +
                #    pauseForm[0][2]), "info")
                """
                The combo form field value is a long integer. We convert it to
                    int because we would receive
                    "TypeError: Form combo field, bad index" at the next
                    instantiation of appuifw.Form().
                """
                myIndex = int(pauseForm[0][2][1])
                SetPauseInterval(pauseIntervalStr[myIndex][1])()
                StoreState()
        except:
            DebugPrintErrorTrace()
        """
        # Doesn't work in Python 2.2 (e.g., PyS60 1.4.5)
        finally:
            appuifw.app.title = ICAM_APP_TITLE
        """
        appuifw.app.title = ICAM_APP_TITLE


def SetUploadedPhotoResolutionIndex_real(aResolutionIndex):
    global photoResolutionStr, photoResolutionIndex, MENU_SELECT_PREFIX

    DebugPrint("SetUploadedPhotoResolutionIndex_real(): " \
                "aResolutionIndex = %d." % aResolutionIndex)

    if (aResolutionIndex < 0) or (aResolutionIndex >= len(photoResolutionStr)):
        return
    """
    if photoResolutionStr[photoResolutionIndex][0][0:len(MENU_SELECT_PREFIX)]\
            == MENU_SELECT_PREFIX:
        photoResolutionStr[photoResolutionIndex][0] = \
          photoResolutionStr[photoResolutionIndex][0][len(MENU_SELECT_PREFIX):]
    photoResolutionStr[aResolutionIndex][0] = MENU_SELECT_PREFIX + \
            photoResolutionStr[aResolutionIndex][0]
    """
    photoResolutionIndex = aResolutionIndex

    """
    global viewFinderSize
    viewFinderSize = photoResolutionStr[photoResolutionIndex][1]
    """

    # SetMenu()
    StoreState()


"""
We define this function as 2 lambdas to make it an callable even if it is
    passed the aResolutionIndex parameter.
"""
SetUploadedPhotoResolutionIndex = lambda aResolutionIndex: lambda : \
    SetUploadedPhotoResolutionIndex_real(aResolutionIndex)


def SetLocalPhotoResolutionIndex(aLocalPhotoResolutionIndex):
    global localPhotoResolutionIndex, cameraPhotoSizes_JPEG_Exif, \
        MENU_SELECT_PREFIX

    DebugPrint("SetLocalPhotoResolutionIndex_real(): " \
            "aLocalPhotoResolutionIndex = %d." % aLocalPhotoResolutionIndex)

    if aLocalPhotoResolutionIndex < 0 or \
        aLocalPhotoResolutionIndex >= len(cameraPhotoSizes_JPEG_Exif[0]):
        return

    localPhotoResolutionIndex[0] = aLocalPhotoResolutionIndex

    DebugPrint("SetLocalPhotoResolutionIndex_real(): made " \
                "localPhotoResolutionIndex[0] = %d." % \
                aLocalPhotoResolutionIndex)

    # SetMenu()
    StoreState()
    SetLocalPhotoResolution()


def SetDigitalZoom_real(aDigitalZoom):
    global digitalZoom

    DebugPrint("SetDigitalZoom_real(): aDigitalZoom = %d." % aDigitalZoom)

    if ANDROID_OS:
        pass
    elif SYMBIAN_S60_OS:
    # elif SYMBIAN_OS:
        if (aDigitalZoom < 0) or (aDigitalZoom >= camera.max_zoom()):
            return
    digitalZoom = aDigitalZoom
    # StartViewFinderForCamera(0, True, True)
    # SetMenu()
    StoreState()


"""
We define this function as 2 lambdas to make it an callable even if it is
    passed the aDigitalZoom parameter.
"""
SetDigitalZoom = lambda aDigitalZoom: lambda : \
    SetDigitalZoom_real(aDigitalZoom)


# If cameraId == 2 then it means audioRecordDuration.
def SetRecordDuration(cameraId, aRecordDuration):
    global videoRecordDuration, audioRecordDuration

    DebugPrint("SetRecordDuration(): cameraId = %d, aRecordDuration = %d." % \
                                                (cameraId, aRecordDuration))

    if aRecordDuration < 0:
        return

    if cameraId == 0 or cameraId == 1:
        videoRecordDuration[cameraId] = aRecordDuration
        if WINDOWS_CE_OS_PYTHONCE:
            StoreConfig_CamAutoCfg_WinCE()
    elif cameraId == 2:
        audioRecordDuration = aRecordDuration

    StoreState()


cameraStr = ["Main", "VGA"]
# If cameraId == 2 then it means audioRecordDuration.
def SetRecordDurationMenu_real(cameraId):
    global videoRecordDuration, audioRecordDuration

    DebugPrint("Entered SetRecordDurationMenu_real(cameraId = %d)." % cameraId)

    if cameraId == 0 or cameraId == 1:
        myText = "Video Record Duration (" + cameraStr[cameraId] + " Camera):"
        initVal = videoRecordDuration[cameraId]
    elif cameraId == 2:
        myText = "Audio Record Duration:"
        initVal = audioRecordDuration

    if ANDROID_OS:
        try:
            # res = int(DialogGetInput(myText, "[seconds]", str(initVal)))
            res = DialogGetInput(myText, "[seconds]", str(initVal))
            DisplayNote("res = %s" % str(res), waitTime=1.0)
            res = int(res)
        except:
            # return
            DebugPrint("Exception in SetRecordDurationMenu_real(). " \
                        "Bailing out...")
            DebugPrintErrorTrace()
            return
    elif SYMBIAN_OS:
        try:
            resStr = appuifw.query(unicode(myText), "number", unicode(initVal))
            if resStr is None:
                return
            res = int(resStr)
        except:
            (exceptionType, exceptionValue, exceptionTraceback) = \
                sys.exc_info()

            DebugPrint("Exception in SetRecordDurationMenu_real(). " \
                        "Bailing out...")
            DebugPrintErrorTrace()
            return

    # SetMenu()
    SetRecordDuration(cameraId, res)
    StoreState()


SetRecordDurationMenu = lambda cameraId: lambda : \
                                        SetRecordDurationMenu_real(cameraId)

# This var is required as global.
durationFormSaved = False


recordConfigFormSaved = False
def RecordConfigMenu():
    global recordConfigFormSaved
    global audioRecordDuration, videoRecordDuration, videoAudioEnabled
    global localVideoModeIndex, localVideoMode

    """
    In case something changed (e.g., on S60 camera2 module doesn't exist), we
        make sure the indices are within the valid values - otherwise, for ex,
        the S60 UI will raise exception.
    """
    for i in range(2):
        if (localVideoModeIndex[i] < 0) or \
                        (localVideoModeIndex[i] >= len(cameraVideoModes[i])):
            localVideoModeIndex[i] = 0
        #if (localVideoMode[i] == []):
        #    localVideoModeIndex[i] = -1

    DebugPrint("Entered RecordConfigMenu():" \
            "  videoRecordDuration[0] = %d, videoRecordDuration[1] = %d\n" % \
                        (videoRecordDuration[0], videoRecordDuration[1]) + \
            "  localVideoModeIndex[0] = %d, localVideoModeIndex[1] = %d\n" % \
                        (localVideoModeIndex[0], localVideoModeIndex[1]) + \
            "  videoAudioEnabled = %s\n" % str(videoAudioEnabled) + \
            "  audioRecordDuration = %d" % audioRecordDuration)
        #localVideoMode is set before being used


    yesNoComboList = [u"No", u"Yes"]

    if ANDROID_OS:
        SetRecordDurationMenu_real(0)

        #TODO!!!! - choose from possible video resolutions

        TITLE_MENU = u"Mute video record?"

        try:
            videoAudioEnabled = 1 - DialogMultipleChoices(TITLE_MENU, \
                                    yesNoComboList, 1 - videoAudioEnabled)

            StoreState()

            #TODO!!!! - call also DisplayRedrawInfo()
        except:
            DebugPrint("Exception in RecordConfigMenu(). Bailing out...")
            DebugPrintErrorTrace()
        """
        DisplayNote("For simple microphone recording, select duration " \
                        "from the Record Config menu.")

        # microphoneComboList[0]
        # (u"Main camera video mode", "combo", (localVideoModeComboList[0],
        #   localVideoModeIndex[0])),

        # (u"Mute video record", "combo", (yesNoComboList,
        #   1 - videoAudioEnabled)),
        """

        SetRecordDurationMenu_real(2)
    elif SYMBIAN_OS:
        """
        From http://wiki.forum.nokia.com/index.php/How_to_use_Form_in_Python_for_S60
            (and also http://www.mobilenin.com/pys60/info_tabs_forms.htm):
        """

        try:
            # Initialize a boolean variable to know whether the form is saved.
            recordConfigFormSaved = False

            localVideoModeComboList = [None, None]

            # (Tested a bit on phone with numCamerasSupported < 2)
            # Create a list to be used in "combo" selection mode.
            for cameraId in range(numCamerasSupported):
                localVideoModeComboList[cameraId] = []

                for videoMode in cameraVideoModes[cameraId]:
                    # print "resPhoto[0] = ", resPhoto[0]
                    # print "resPhoto[1] = ", resPhoto[1]
                    """
                    localVideoModeComboList[cameraId] += [u"(%d, %d, %.1f)" \
                        % (videoMode["size"][0],
                            videoMode["size"][1], videoMode["rate"])]
                    """
                    localVideoModeComboList[cameraId].append( \
                        u"(%d x %d @ %.0f fps)" % (videoMode["size"][0], \
                        videoMode["size"][1], videoMode["rate"]) )
        except:
            DebugPrintErrorTrace()

        try:
            myFields = []

            if numCamerasSupported >= 1:
                myFields = [
                    (u"Main camera record duration", "number",
                         videoRecordDuration[0]),
                    (u"Main camera video mode", "combo",
                         (localVideoModeComboList[0], localVideoModeIndex[0]))
                ]

            if numCamerasSupported == 2:
                myFields += [
                    (u"VGA camera record duration", "number",
                         videoRecordDuration[1]),
                    (u"VGA camera video mode", "combo",
                        (localVideoModeComboList[1], localVideoModeIndex[1]))
                ]

            myFields += [
                (u"Mute video record", "combo",
                    (yesNoComboList, 1 - videoAudioEnabled)),
                (u"Microphone record duration", "number",
                    audioRecordDuration)
            ]

            # appuifw.app.title = u"Select Record Durations"
            appuifw.app.title = u"Record Config"

            # Creates the form
            """
            recordConfigForm = appuifw.Form(myFields,
                flags=appuifw.FFormEditModeOnly)
            """
            #recordConfigForm = appuifw.Form(myFields, appuifw.FFormEditModeOnly)
            #recordConfigForm = appuifw.Form(myFields, appuifw.FFormDoubleSpaced)
            recordConfigForm = appuifw.Form(myFields,
                                        appuifw.FFormEditModeOnly | \
                                        appuifw.FFormDoubleSpaced)

            # Define a function to be called when the form is saved.
            def RecordConfigFormSaved(arg):
                global recordConfigFormSaved, canvas

                recordConfigFormSaved = True
                # appuifw.app.body = canvas
                RedrawHandler(None)
                return True

            # Assign the save function
            recordConfigForm.save_hook = RecordConfigFormSaved

            #Show the form. This operation is blocking until we close the form.
            recordConfigForm.execute()

            # After the form is saved and closed, display the information.
            if recordConfigFormSaved == True:
                # print recordConfigForm[0][2]
                """
                appuifw.note(unicode("Google Username: " +
                                recordConfigForm[0][2]), "info")

                videoRecordDuration[0] = ...
                videoRecordDuration[1] = recordConfigForm[1][2]
                audioRecordDuration = recordConfigForm[2][2]
                """

                """
                The combo form field value is a long integer.
                    We convert it to int because we would receive
                    "TypeError: Form combo field, bad index" at the next
                    instantiation of appuifw.Form().
                """
                SetRecordDuration(0, int(recordConfigForm[0][2]))

                localVideoModeIndex[0] = int(recordConfigForm[1][2][1])
                videoMode = cameraVideoModes[0][localVideoModeIndex[0]]
                localVideoMode[0] = ((videoMode["size"][0],
                                        videoMode["size"][1]),
                                        videoMode["rate"])

                DebugPrint("RecordConfigMenu(): localVideoMode[0] = %s." % \
                                                    str(localVideoMode[0]))

                SetRecordDuration(1, int(recordConfigForm[2][2]))

                localVideoModeIndex[1] = int(recordConfigForm[3][2][1])
                videoMode = cameraVideoModes[1][localVideoModeIndex[1]]

                localVideoMode[1] = ((videoMode["size"][0],
                        videoMode["size"][1]), videoMode["rate"])

                DebugPrint("RecordConfigMenu(): localVideoMode[1] = %s." % \
                                                    str(localVideoMode[1]))

                videoAudioEnabled = 1 - int(recordConfigForm[4][2][1])
                SetRecordDuration(2, int(recordConfigForm[5][2]))

                StoreState()
        except:
            DebugPrintErrorTrace()
        """
        # Doesn't work in Python 2.2 (e.g., PyS60 1.4.5)
        finally:
            appuifw.app.title = ICAM_APP_TITLE
        """
        appuifw.app.title = ICAM_APP_TITLE


# This var is required as global.
cameraModesFormSaved = False

def CaptureWhatMenu():
    global cameraModesFormSaved
    global cameraMode, readGPS, logAccelerometerAndRotationSensors

    DebugPrint("Entered CaptureWhatMenu().")

    """
    From http://wiki.forum.nokia.com/index.php/How_to_use_Form_in_Python_for_S60
        (and also http://www.mobilenin.com/pys60/info_tabs_forms.htm):
    """
    # u"Media Storage"
    TITLE_MENU = u"Capture What"

    cameraModesComboList = [u"None", u"Video", u"Photo",
                            u"Both photo and video"]
    microphoneComboList = [u"[Select duration from Record Config]"]

    if ANDROID_OS:
        # resMenu = None
        try:
            cameraMode[0] = DialogMultipleChoices(TITLE_MENU, \
                            cameraModesComboList, int(cameraMode[0]))

            # val = int(DialogGetInput(myText, "[seconds]", str(initVal)))

            StoreState()
        except:
            DebugPrint("Exception in CaptureWhatMenu(). Bailing out...")
            DebugPrintErrorTrace()

        DisplayNote("For simple microphone recording, select duration from " \
                    "the Record Config menu.")  # microphoneComboList[0]
    elif SYMBIAN_OS:
        try:
            # Initialize a boolean variable to know whether the form is saved.
            cameraModesFormSaved = False

            notAvailableComboList = [u"Not Available"]
            noComboList = [u"No"]
            yesNoComboList = [u"No", u"Yes"]

            # Samsung, Nokia E7, 6120 and N82.
            if deviceId in [IMEI_G810, IMEI_E7, IMEI_N95, IMEI_N82]:
                gpsComboList = yesNoComboList
            else:
                gpsComboList = noComboList

            # Assuming numCamerasSupported == 2.
            myFields = [(u"Main camera", "combo",
                            (cameraModesComboList, cameraMode[0])),
                        (u"VGA camera", "combo",
                            (cameraModesComboList, cameraMode[1])),
                            (u"Microphone", "combo", (microphoneComboList, 0)),
                        (u"Read GPS coordinates", "combo",
                            (gpsComboList, readGPS)),
                        # (u"Read GPS coordinates", "combo",
                        #   (noComboList, readGPS)),
                        (u"Accelerometer & Rotation", "combo",
                            (noComboList, logAccelerometerAndRotationSensors))]
                        #(u"Accelerometer & Rotation", "combo", (yesNoComboList,
                        #   logAccelerometerAndRotationSensors))
            if numCamerasSupported == 0:
                myFields[0] = (u"Main camera", "combo",
                        (notAvailableComboList, 0))
            if numCamerasSupported < 2:
                myFields[1] = (u"VGA camera", "combo",
                               (notAvailableComboList, 0))

            appuifw.app.title = TITLE_MENU

            # Creates the form
            """
            durationForm = appuifw.Form(myFields,
                flags=appuifw.FFormEditModeOnly)
            """
            # durationForm = appuifw.Form(myFields, appuifw.FFormEditModeOnly)
            """
            cameraModesForm = appuifw.Form(myFields,
                appuifw.FFormDoubleSpaced)
            """
            cameraModesForm = appuifw.Form(myFields,
                    appuifw.FFormEditModeOnly
                    | appuifw.FFormDoubleSpaced)

            # Define a function to be called when the form is saved
            def CameraModeFormSaved(arg):
                global cameraModesFormSaved
                cameraModesFormSaved = True
                return True

            # Assign the save function
            cameraModesForm.save_hook = CameraModeFormSaved

            #Show the form. This operation is blocking until we close the form.
            cameraModesForm.execute()

            # After the form is saved and closed, display the information.
            if cameraModesFormSaved == True:
                # print durationForm[0][2]
                """
                appuifw.note(unicode("Google Username: " +
                                durationForm[0][2]), "info")
                """
                """
                The combo form field value is a long integer. We convert it to
                    int because we would receive
                    "TypeError: Form combo field, bad index"
                    at the next instantiation of appuifw.Form().
                """
                cameraMode[0] = int(cameraModesForm[0][2][1])
                cameraMode[1] = int(cameraModesForm[1][2][1])

                readGPS = int(cameraModesForm[3][2][1])
                #if phoneModel in ["NokiaN95", "NokiaN82", "NokiaN8",
                #                   "NokiaE7"]:
                if readGPS:
                    StartGPS()
                else:
                    StopGPS()

                StoreState()
        except:
            DebugPrintErrorTrace()
        """
        # Doesn't work in Python 2.2 (e.g., PyS60 1.4.5).
        finally:
            appuifw.app.title = ICAM_APP_TITLE
        """
        appuifw.app.title = ICAM_APP_TITLE

    DebugPrint("Exiting CaptureWhatMenu().")


def SetPhotoQuality_real(aQuality):
    global photoQuality

    if (aQuality < 0) or (aQuality > 100):
        return

    photoQuality = aQuality
    # SetMenu()
    StoreState()


"""
We define this function as 2 lambdas to make it an callable even if it is
    passed the aQualityIndex parameter.
"""
SetPhotoQuality = lambda aQuality: lambda : \
    SetPhotoQuality_real(aQuality)

photoFormSaved = False  # This var is required as global.


def PhotoConfigMenu():
    global photoFormSaved
    global localPhotoResolutionIndex, photoResolutionIndex, photoQuality

    DebugPrint("Entered PhotoConfigMenu().")

    if SYMBIAN_OS:
        """
        From http://wiki.forum.nokia.com/index.php/How_to_use_Form_in_Python_for_S60
            (and also http://www.mobilenin.com/pys60/info_tabs_forms.htm):
        """
        try:
            # Initialize a boolean variable to know whether the form is saved.
            photoFormSaved = False

            localComboList = [None, None]
            # sentComboList = [None, None]
            sentComboList = None

            # Create a list to be used in "combo" selection mode.
            for cameraId in range(numCamerasSupported):
                localComboList[cameraId] = []

                # This is IMPORTANT
                if cameraId == 0:
                    if phoneModel == "SamsungSGH-G810":
                        cameraPhotoSizes = \
                            cameraPhotoSizes_RGB24[0]
                    else:
                        cameraPhotoSizes = \
                            cameraPhotoSizes_JPEG_Exif[0]
                elif cameraId == 1:
                    cameraPhotoSizes = cameraPhotoSizes_RGB24[1]

                for resPhoto in cameraPhotoSizes:
                    """
                    Example:
                    cameraPhotoSizes_JPEG_Exif[0] = [
                                                        (2592, 1944),
                                                        (2048, 1536),
                                                        (1600, 1200),
                                                        (1024, 768),
                                                        (640, 480)
                                                    ]
                    """
                    # print "resPhoto[0] = ", resPhoto[0]
                    # print "resPhoto[1] = ", resPhoto[1]
                    localComboList[cameraId].append( u"(%d, %d)" % \
                                                (resPhoto[0], resPhoto[1]) )

                DebugPrint( "PhotoConfigMenu(): localComboList[%d] = %s." % \
                        (cameraId, str(localComboList[cameraId])) )

            sentComboList = []
            """
            Example:
                cameraPhotoSizes_JPEG_Exif[0] = [
                                                    (2592, 1944), (2048, 1536),
                                                    (1600, 1200), (1024, 768),
                                                    (640, 480)
                                                ]
            """
            for resPhoto in photoResolutionStr:
                # print "resPhoto[0] = ", resPhoto[0]
                # print "resPhoto[1] = ", resPhoto[1]
                sentComboList.append( u"%s" % resPhoto[0] )

            DebugPrint( "PhotoConfigMenu(): sentComboList = %s." % \
                                                str(sentComboList) )

            if numCamerasSupported >= 1:
                """
                            (u"", "text", \
                                u"Local Photo Resolution (Each camera)"),
                            (u"Local Photo Resolutions", \
                                "text", u""),
                            (u"Main camera", "number", \
                                videoRecordDuration[0]),
                            (u"Main camera", "combo", (localComboList[0], \
                                localPhotoResolutionIndex[0]))]
                """
                myFields = [ \
                            ( u"Local Photo Resolutions - Main camera", \
                                "combo", (localComboList[0], \
                                            localPhotoResolutionIndex[0]) ) \
                        ]

            if numCamerasSupported == 2:
                """
                            (u"VGA camera", "combo", (localComboList[1], \
                                localPhotoResolutionIndex[1]))]
                """
                myFields += [ \
                            ( u"Local Photo Resolutions - VGA camera", \
                             "combo", (localComboList[1], \
                                        localPhotoResolutionIndex[1]) ) \
                        ]
            else:
                #             (u"VGA camera", "text", u"n/a")]
                myFields += [ \
                            ( u"Local Photo Resolutions - VGA camera", \
                             "text", u"[N/A]" )]

            if numCamerasSupported >= 1:
                """
                            (u"", "text", \
                               u"Sent Photo Resolution (Both cameras)"),
                """
                myFields += [ \
                            ( u"Sent Photo Resolution - Both cameras", \
                             "combo", (sentComboList, \
                                        photoResolutionIndex) ) ]

            """
            if numCamerasSupported == 2:
                myFields += [ \
                           (u"VGA", "combo", (sentComboList[1], \
                                                photoResolutionIndex[1]))]
            else:
                myFields += [
                           (u"VGA", "text", u"")]
            """
            if numCamerasSupported >= 1:
                #                 (u"", "text", u"Sent Photo Quality"),
                myFields += [ \
                                ( u"Sent Photo Quality - Both cameras", \
                                 "number", photoQuality ) ]

            DebugPrint("PhotoConfigMenu(): myFields = %s." % str(myFields))

            appuifw.app.title = u"Photo Config"

            # Creates the form.
            #photoForm = appuifw.Form(myFields,flags=appuifw.FFormEditModeOnly)
            #photoForm = appuifw.Form(myFields, appuifw.FFormEditModeOnly)
            #photoForm = appuifw.Form(myFields, appuifw.FFormDoubleSpaced)
            photoForm = appuifw.Form(myFields, appuifw.FFormEditModeOnly | \
                                                appuifw.FFormDoubleSpaced)

            # Define a function to be called when the form is saved.
            def PhotoFormSaved(arg):
                global photoFormSaved
                photoFormSaved = True
                return True

            # Assign the save function
            photoForm.save_hook = PhotoFormSaved

            #Show the form. This operation is blocking until we close the form.
            photoForm.execute()

            # After the form is saved and closed, display the information
            if photoFormSaved == True:
                #print photoForm[0][2]
                #appuifw.note(unicode("Google Username: " + photoForm[0][2]),
                #              "info")
                """
                The combo form field value is a long integer. We convert it to
                    int because we would receive
                    "TypeError: Form combo field, bad index"
                    at the next instantiation of appuifw.Form().
                """
                localPhotoResolutionIndex[0] = int(photoForm[0][2][1])

                if numCamerasSupported == 2:
                    localPhotoResolutionIndex[1] = int(photoForm[1][2][1])

                photoResolutionIndex = int(photoForm[2][2][1])
                SetLocalPhotoResolution()

                """
                DebugPrint("PhotoConfigMenu(): " \
                            "localPhotoResolutionIndex[0] = %d, " \
                            "len(cameraPhotoSizes) = %d." % \
                            (cameraId, localPhotoResolutionIndex[0],
                                len(cameraPhotoSizes)))

                selectionListResolution = []
                for i in range(len(cameraPhotoSizes)):
                    if i == localPhotoResolutionIndex[cameraId]:
                        selectionListResolution += [unicode(MENU_SELECT_PREFIX)
                                            + unicode(cameraPhotoSizes[i])]
                    else:
                        selectionListResolution += \
                            [unicode(cameraPhotoSizes[i])]

                index = appuifw.selection_list(selectionListResolution, 0)
                if index is not None:
                    SetLocalPhotoResolutionIndex(index)()
                """

                photoQuality = int(photoForm[3][2])
                StoreState()
        except:
            DebugPrintErrorTrace()
        """
        # Doesn't work in Python 2.2 (e.g., PyS60 1.4.5).
        finally:
            appuifw.app.title = ICAM_APP_TITLE
        """
        appuifw.app.title = ICAM_APP_TITLE


def UploadInboxSMSes(cleanUndesiredSMSes=False):
    global deviceId
    global inboxIsImported

    DebugPrint("Entered UploadInboxSMSes(): inboxIsImported = %d." % \
                                                        inboxIsImported)

    if SYMBIAN_OS:
        if inboxIsImported == False:
            return

        try:
            myMailbox = inbox.Inbox(inbox.EInbox)

            messageIdList = myMailbox.sms_messages()
            myText = "The phone Inbox has %d SMSes.<br/>\n" % \
                                                len(messageIdList)
            myText += "The phone Inbox SMSes have message IDs: " + \
                                            str(messageIdList) + "<br/>\n"

            messageCounter = 0
            for messageId in messageIdList:
                try:
                    myText += "  " + str(messageId) + "<br/>\n"
                except:
                    (exceptionType, exceptionValue, exceptionTraceback) = \
                        sys.exc_info()

                    myText += "Exception: exceptionTraceback = %s, " \
                            "exceptionType = %s, " \
                            "exceptionValue = %s.<br/>\n" % \
                            (repr(traceback.format_tb(exceptionTraceback)),
                                str(exceptionType), str(exceptionValue))

                    DebugPrintErrorTrace()

                try:
                    myText += "    " + myMailbox.address(messageId) + "<br/>\n"
                except:
                    (exceptionType, exceptionValue, exceptionTraceback) = \
                        sys.exc_info()

                    myText += "Exception: exceptionTraceback = %s, " \
                                "exceptionType = %s, " \
                                "exceptionValue = %s.<br/>\n" % \
                                (repr(traceback.format_tb(exceptionTraceback)),
                                   str(exceptionType), str(exceptionValue))

                    DebugPrintErrorTrace()

                try:
                    myText += "    " + myMailbox.content(messageId) + "<br/>\n"
                except:
                    (exceptionType, exceptionValue, exceptionTraceback) = \
                        sys.exc_info()

                    myText += "Exception: exceptionTraceback = %s, " \
                                "exceptionType = %s, " \
                                "exceptionValue = %s.<br/>\n" % \
                                (repr(traceback.format_tb(exceptionTraceback)),
                                   str(exceptionType), str(exceptionValue))

                    DebugPrintErrorTrace()

                try:
                    myText += "    " + time.ctime(myMailbox.time(messageId)) \
                                    + "<br/>\n"
                except:
                    (exceptionType, exceptionValue, exceptionTraceback) = \
                        sys.exc_info()

                    myText += "Exception: exceptionTraceback = %s, " \
                                "exceptionType = %s, " \
                                "exceptionValue = %s.<br/>\n" % \
                                (repr(traceback.format_tb(exceptionTraceback)),
                                   str(exceptionType), str(exceptionValue))

                    DebugPrintErrorTrace()

                if cleanUndesiredSMSes:
                    try:
                        if str(myMailbox.content(messageId)).startswith( \
                                "Serviciul de date este deja activ."):
                            """
                            if str(myMailbox.address(messageId)) == "302" and
                                str(myMailbox.content(messageId)).startswith(
                                    "Serviciul de date este deja activ."):
                            """
                            DebugPrint("UploadInboxSMSes(): deleting message " \
                                        "with messageId = %d since it " \
                                        "corresponds to cleaning criteria." % \
                                        messageId)

                            myMailbox.delete(messageId)
                    except:
                        (exceptionType, exceptionValue, exceptionTraceback) = \
                            sys.exc_info()

                        myText += "Exception: exceptionTraceback = %s, " \
                                    "exceptionType = %s, " \
                                    "exceptionValue = %s.<br/>\n" % \
                                    (repr(traceback.format_tb(
                                        exceptionTraceback)),
                                        str(exceptionType), str(exceptionValue))

                        DebugPrintErrorTrace()

                messageCounter += 1

            try:
                myText = myText.encode("ascii", "ignore")
            except:
                (exceptionType, exceptionValue, exceptionTraceback) = \
                    sys.exc_info()

                myText += "Exception: exceptionTraceback = %s, " \
                            "exceptionType = %s, " \
                            "exceptionValue = %s.<br/>\n" % \
                            (repr(traceback.format_tb(exceptionTraceback)),
                               str(exceptionType), str(exceptionValue))

                DebugPrintErrorTrace()

            # myText = str(myText)

            myDateTime = GetCurrentDateTimeStringWithMilliseconds()

            """
            myDateTime = time.strftime("%Y_%m_%d_%H_%M_%S",
                                        GetCurrentDateTime())
            """
            fileName = "SMSes_Inbox_" + myDateTime + ".txt"
            pathFileName = LOCAL_FOLDER + "/" + fileName

            fOutput = open(pathFileName, "wb")
            fOutput.write(myText)
            fOutput.close()

            try:
                if not os.path.exists(LOCAL_FOLDER_SENT_LOGS):
                    os.makedirs(LOCAL_FOLDER_SENT_LOGS)
            except:
                DebugPrintErrorTrace()
            """
            if UploadFile(pathFileName, ICAM_SERVER_NAME,
                            WEBPAGE_UL_GZIPPED_FILE) != -1:
                MoveFileBetweenAnyDrives(pathFileName,
                    LOCAL_FOLDER_SENT_LOGS + "/" + fileName)
            """
            UploadFile(pathFileName, ICAM_SERVER_NAME, WEBPAGE_UL_GZIPPED_FILE)

            MoveFileBetweenAnyDrives(pathFileName,
                    LOCAL_FOLDER_SENT_LOGS + "/" + fileName)

            """
            if MY_DEBUG_UPLOAD_MSG:
                UploadGZippedData(deviceId, myText, ICAM_SERVER_NAME,
                                    WEBPAGE_UL_GZIPPED_TEXT, None)
            """
        except:
            (exceptionType, exceptionValue, exceptionTraceback) = \
                    sys.exc_info()

            if MY_DEBUG_UPLOAD_MSG:
                UploadGZippedData(deviceId,
                        "Exception in UploadInboxSMSes() - details: " \
                        "free_ram = %d. exceptionTraceback = %s, " \
                        "exceptionType = %s, exceptionValue = %s. " \
                        "Bailing out..." %
                        (GetFreeRAM(),
                            repr(traceback.format_tb(exceptionTraceback)),
                            str(exceptionType), str(exceptionValue)),
                            ICAM_SERVER_NAME, WEBPAGE_UL_GZIPPED_TEXT, None)

            DebugPrint("Exception in UploadInboxSMSes(). Bailing out...")
            DebugPrintErrorTrace()


"""
Not much used - ex use: employ it to receive a .WAV file from the
    server to be played on the phone script (e.g., an updated iCam.Py script)
    and run it, an extension, etc.
"""
def DownloadFile(fileName):
    global ICAM_SERVER_NAME, accessPointName
    global deviceId

    if NoInternetConnection():
        return

    try:
        PetWatchdog()

        dataCompressed = urllib.urlopen("http://" + ICAM_SERVER_NAME + \
                WEBPAGE_DL_GZIPPED_FILE + "?deviceId=" + deviceId + \
                "&filename=" + fileName).read()

        dataUncompressed = dataCompressed.decode("zlib")

        LOCAL_FOLDER_FILES_FROM_SERVER = LOCAL_FOLDER + "/FilesFromServer"

        try:
            if not os.path.exists(LOCAL_FOLDER_FILES_FROM_SERVER):
                os.makedirs(LOCAL_FOLDER_FILES_FROM_SERVER)
        except:
            DebugPrintErrorTrace()

        fOutput = open(LOCAL_FOLDER_FILES_FROM_SERVER + "/" + fileName, "wb")
        fOutput.write(dataUncompressed)
        fOutput.close()
    except:
        (exceptionType, exceptionValue, exceptionTraceback) = \
            sys.exc_info()

        myText = "Exception in DownloadFile. Details: time = %s, " \
                    "free_ram = %d. %s." % \
                    (GetCurrentDateTimeStringNice(), GetFreeRAM(),
                       repr(traceback.format_tb(exceptionTraceback)))

        if MY_DEBUG_UPLOAD_MSG:
            UploadGZippedData(deviceId, myText, ICAM_SERVER_NAME,
                              WEBPAGE_UL_GZIPPED_TEXT, None)

        DebugPrint(myText)

        if MY_DEBUG_STDERR:
            # traceback.print_exc()
            sys.stderr.write(myText + "\n")
            sys.stderr.flush()


"""
Change User-Agent
    (see http://docs.python.org/library/urllib.html and
    http://diveintopython3.org/http-web-services.html).
By default urllib.urlopen sends a User-Agent string "Python-urllib/1.17"
    (see /var/log/apache2/access.log for log entries when it calls cmd.php).
"""
class AppURLopener(urllib.FancyURLopener):
    version = ""


urllib._urlopener = AppURLopener()

# (S60_EDITION[0] < 3)):
#if SYMBIAN_OS and SYMBIAN_S60_2ND_ED:
if sys.version_info[0 : 2] == (2, 2):
    class MyListIterClass:
        """
        Helper class for Python 2.2.

        This class has to be put outside ExecuteCommands() otherwise we get
            exception: "SyntaxError: unqualified exec is not allowed in
            function 'ExecuteCommands' it contains a nested function with
            free variables" .

        Python 2.2.2 (e.g., PyS60 1.4.5) does not have iterator defined for list -
            although they say that __iter__ exists from Python 2.2 in
                http://docs.python.org/release/2.5.2/lib/typeiter.html,
                http://www.python.org/dev/peps/pep-0234/ and
                http://docs.python.org/library/stdtypes.html.
        """
        def __init__(self, aList):
            self.myList = aList
            self.myIndex = 0

        def next(self):
            if self.myIndex >= len(self.myList):
                raise StopIteration

            res = self.myList[self.myIndex]
            self.myIndex += 1
            return res

        def __length_hint__(self):
            return len(self.myList) - self.myIndex


def ExecuteCommands(cmdString, fastExec=False):
    global MY_DEBUG_STDOUT, MY_DEBUG_STDERR, MY_DEBUG_STDERR_2, \
        MY_DEBUG_UPLOAD_MSG
    global MAX_NUM_HOTSPOTS
    global deviceId, ICAM_SERVER_NAME, WEBPAGE_UL_GZIPPED_TEXT
    global modeManagerIsEnabled
    global internetUploadMaxErrors
    global NUM_UNSENT_PACKETS_BEFORE_DOWNLOAD_COMMANDS
    global uploadUnsentData
    global saveUnsentPackets
    global uploadHowManyOfLatestBluetoothMessages
    global pauseIntervalGdata
    global BATTERY_LEVEL_THRESHOLD
    global cameraMode
    global exposureIndex
    global whiteBalanceIndex
    global flashIndex
    global videoAudioEnabled
    global localVideoModeIndex, localVideoMode, cameraVideoModes
    global localPhotoResolutionIndex
    global bluetoothMode
    global bluetoothServerAddress
    global burstModeIsStarted
    global numHotspots
    global differentPixelsPercentageThreshold
    global hotspot
    global storeLocallyMedia
    global uploadMediaToYouTube, uploadMediaToPicasa, useiCamServer
    global googleUsername
    global googlePasswordEncrypted
    global googleKeywords
    global googleMediaPrivate
    global dawnTimeVec
    global duskTimeVec

    if (cmdString is None) or (len(cmdString) < 5):
        return False

    if fastExec == False:
        myText = "ExecuteCommands(): Received commands at %s: %s." % \
                    (GetCurrentDateTimeStringNice(), cmdString)

        DebugPrint(myText)

        if MY_DEBUG_UPLOAD_MSG:
            UploadGZippedData(deviceId, myText, ICAM_SERVER_NAME,
                              WEBPAGE_UL_GZIPPED_TEXT, None)

    try:
        lineList = cmdString.splitlines()
    except:
        DebugPrintErrorTrace()
        return False

    #res = 0

    # Python 2.2 does not have iterator for list, so we create a helper class.
    # (S60_EDITION[0] < 3)):
    #if SYMBIAN_OS and SYMBIAN_S60_2ND_ED:
    if sys.version_info[0 : 2] == (2, 2):
        cmdIterator = MyListIterClass(lineList)
    else:
        cmdIterator = lineList.__iter__()

    """
    Inspired from
       http://stackoverflow.com/questions/1292189/how-does-python-for-loop-work

    while True:
        try:
            line = cmdIterator.next()
        except StopIteration:
            # StopIteration exception is raised after last element
            break

        print cmdIterator.__length_hint__()
        # loop code
        print line
    """

    """
    Python doesn't have hasNext() method - see
        http://stackoverflow.com/questions/1966591/hasnext-in-python-iterators
    """
    while cmdIterator.__length_hint__() > 0:
        try:
            cmdLine = cmdIterator.next()
            # print cmdIterator.__length_hint__()
            tokens = cmdLine.split(" ")

            DebugPrint("ExecuteCommands(): tokens = %s." % str(tokens))

            if tokens[0] == "quit-application":
                Quit()

            elif tokens[0] == "restart-phone":
                RestartPhone()

            elif tokens[0] == "set-startAutomatically":
                """
                I think it is not a good idea to do have it since we don't
                    want to give the possibility to control remotely the
                    device.

                    global startAutomatically
                    startAutomatically = int(tokens[1])
                """
                pass
            elif tokens[0] == "set-internetUploadMaxErrors":
                internetUploadMaxErrors = int(tokens[1])

            elif tokens[0] \
                == "set-NUM_UNSENT_PACKETS_BEFORE_DOWNLOAD_COMMANDS":
                NUM_UNSENT_PACKETS_BEFORE_DOWNLOAD_COMMANDS = \
                    int(tokens[1])

            elif tokens[0] == "set-uploadUnsentData":
                """
                Note: 0 - send none; 1 - send unsent files from Unsent;
                    2 - send unsent logs; 3 - send ALL
                """
                uploadUnsentData = int(tokens[1])

            elif tokens[0] == "set-saveUnsentPackets":
                """
                # 0 - None, 1 - All; 2 - wo .txm
                """
                saveUnsentPackets = int(tokens[1])

            elif tokens[0] == "upload-how-many-of-latest-bluetooth-messages":
                """
                Note: 0 - don't upload, if -1 - upload all BUT in chrono order,
                    if > 0 then upload that many latest.
                """
                uploadHowManyOfLatestBluetoothMessages = int(tokens[1])

            elif tokens[0] == "send-file-via-bluetooth":
                BluetoothClientDiscoverServer(tokens[1])

                #!!!!TODO
                """
                BluetoothUploadGZippedData(tokens[1],
                        cmdLine[len(tokens[0]) + len(tokens[1]) + 2:],
                        COMMANDS_FILENAME, newMode=NEW_BT_FORMAT)
                """

            elif tokens[0] == "send-command-via-bluetooth":
                """
                Note that BluetoothUploadGZippedData() calls also the
                    BluetoothClientDiscoverServer(), but since we are
                    normally executing this command from the BT server and
                    it can send commands to several BT clients, then need to
                    reinitialize each time bluetoothServerOPPServicePort - 
                    we normally don't recompute it otherwise, since we assume
                    the phone is BT client.
                """
                BluetoothClientDiscoverServer(tokens[1])

                BluetoothUploadGZippedData(tokens[1],
                        cmdLine[len(tokens[0]) + len(tokens[1]) + 2:],
                        COMMANDS_FILENAME, newMode=NEW_BT_FORMAT)

            elif tokens[0] == "send-commands-via-bluetooth":
                # print tokens[2:]
                # print tokens[2:]
                btCmd = cmdLine[len(tokens[0]) + len(tokens[1]) + 2:]

                # print "Initially btCmd =", btCmd
                while cmdIterator.__length_hint__() > 0:
                    cmdLineNew = cmdIterator.next()

                    if cmdLineNew == "send-commands-via-bluetooth-endline":
                        break
                    else:
                        btCmd += "\n" + cmdLineNew

                DebugPrint("ExecuteCommands(): btCmd = %s." % btCmd)

                """
                Note that BluetoothUploadGZippedData() calls also the
                    BluetoothClientDiscoverServer(), but since we are
                    normally executing this command from the BT server and
                    it can send commands to several BT clients, then need to
                    reinitialize each time bluetoothServerOPPServicePort - 
                    we normally don't recompute it otherwise, since we assume
                    the phone is BT client.
                """
                BluetoothClientDiscoverServer(tokens[1])

                BluetoothUploadGZippedData(tokens[1], btCmd, \
                                    COMMANDS_FILENAME, newMode=NEW_BT_FORMAT)

            elif tokens[0] == "set-logging-level":
                MY_DEBUG_STDOUT = int(tokens[1])
                MY_DEBUG_STDERR = int(tokens[2])
                MY_DEBUG_STDERR_2 = int(tokens[3])
                MY_DEBUG_UPLOAD_MSG = int(tokens[4])

            elif tokens[0] == "set-pause-interval":
                # myTimer.cancel()
                SetPauseInterval(int(tokens[1]))()

            elif tokens[0] == "set-pauseIntervalGdata":
                # res = 1
                pauseIntervalGdata = int(tokens[1])

            elif tokens[0] == "set-battery-level-threshold":
                BATTERY_LEVEL_THRESHOLD = int(tokens[1])

            elif tokens[0] == "set-camera-mode":
                """
                Note: 0 = None, 1 = Only Video, 2 = Only Photo,
                    3 = Both Photo and Video
                """
                cameraMode[int(tokens[1])] = int(tokens[2])

            elif tokens[0] == "set-photo-resolution-index":
                SetUploadedPhotoResolutionIndex(int(tokens[1]))()

            elif tokens[0] == "set-photo-quality":
                SetPhotoQuality(int(tokens[1]))()

            elif tokens[0] == "set-photo-mode-index":
                pass

            elif tokens[0] == "set-digital-zoom":
                SetDigitalZoom(int(tokens[1]))()

            elif tokens[0] == "set-camera-exposure-index":
                cameraId = int(tokens[1])
                exposureIndex[cameraId] = int(tokens[2])

            elif tokens[0] == "set-camera-whitebalance-index":
                cameraId = int(tokens[1])
                whiteBalanceIndex[cameraId] = int(tokens[2])

            elif tokens[0] == "set-flash-index":
                flashIndex = int(tokens[1])

            elif tokens[0] == "set-camera-video-record-duration":
                cameraId = int(tokens[1])
                SetRecordDuration(cameraId, int(tokens[2]))

            elif tokens[0] == "set-video-mute":
                videoAudioEnabled = 1 - int(tokens[1])

                DebugPrint("ExecuteCommands(): made videoAudioEnabled = %d." % \
                                                            videoAudioEnabled)

            elif tokens[0] == "set-local-video-mode-index":
                cameraId = int(tokens[1])
                localVideoModeIndex[cameraId] = int(tokens[2])
                """
                localVideoMode[cameraId] = \
                    cameraVideoModes[localVideoModeIndex[cameraId]]

                localVideoMode[cameraId][0] = \
                    cameraVideoModes[cameraId][
                        localVideoModeIndex[cameraId]]["size"]

                localVideoMode[cameraId][1] = cameraVideoModes[cameraId][
                        localVideoModeIndex[cameraId]]["rate"]
                """
                localVideoMode[cameraId] = \
                    ( cameraVideoModes[cameraId][
                        localVideoModeIndex[cameraId]]["size"],
                     cameraVideoModes[cameraId][
                        localVideoModeIndex[cameraId]]["rate"] )

            elif tokens[0] == "set-local-photo-resolution-index":
                # SetLocalPhotoResolution()
                cameraId = int(tokens[1])
                localPhotoResolutionIndex[cameraId] = int(tokens[2])
                SetLocalPhotoResolution()

            elif tokens[0] == "set-audio-record-duration":
                SetRecordDuration(2, int(tokens[1]))

            elif tokens[0] == "set-bluetooth-mode":
                # Note: bluetoothMode is: 0 is None; 1 is BT server; 2 is BT client.
                bluetoothMode = int(tokens[1])

            elif tokens[0] == "set-bluetoothServerAddress":
                # Ex: bluetoothServerAddress is "ff:ff:ff:ab:cd:ef"
                bluetoothServerAddress = tokens[1]

            elif tokens[0] == "upload-file":
                UploadFile(tokens[1], ICAM_SERVER_NAME,
                           WEBPAGE_UL_GZIPPED_FILE)

            elif tokens[0] == "upload-inbox-smses":
                if len(tokens) > 1 and \
                    tokens[1] == "and-clean-undesired-smses":
                # if tokens[1] == "and-clean-undesired-smses":
                    UploadInboxSMSes(True)
                else:
                    UploadInboxSMSes(False)

            elif tokens[0] == "retrieve-gps-coordinates":
                StartGPS()

            elif tokens[0] == "download-file":
                DownloadFile(tokens[1])

            elif tokens[0] == "play-audio-file":
                soundRecord = audio.Sound.open(unicode(tokens[1]))

                DebugPrint("ExecuteCommands(): play-audio-file - starting to " \
                            "play file %s of size %.2f" % \
                            (tokens[1],
                                float(soundRecord.duration()) / 1000000))

                # PlayAudioFile()
                soundRecord.play()
                # soundRecord.play(times=1, interval=0, callback=validate)

                """
                If the audio file is played several times, interval gives the
                    time interval between the subsequent plays in microseconds.
                """
                # play([times=1, interval=0, callback=None])

                """
                This is required because otherwise the file stops playing
                    (probably because of SetMenu() at the end of this
                    function).
                """
                SleepAndPetWatchdog((soundRecord.duration() + 500000) /
                                                                1000000)

                """
                Inspired from
                    Z:\1PhD\ReVival\111111111Src_examples\PyS60_2_0_0\winscw\c\data\python\test\test_audio.py
                """

                """
                max_volume = self.sound_object.max_volume()
                set_volume = max_volume - 2
                self.sound_object.set_volume(set_volume)
                """

                """
                time_of_playback = self.sound_object.duration()
                # Subtract 3 seconds(expressed in micro seconds) from the total
                # duration of the track and start playback from this position.
                start_position = time_of_playback - 3000000L
                self.sound_object.set_position(start_position)
                """

            elif tokens[0] == "set-server":
                ICAM_SERVER_NAME = tokens[1]

            elif tokens[0] == "get-date-and-time":
                myText = "Date and time is: " \
                    + GetCurrentDateTimeStringWithMilliseconds() + "."
                # if MY_DEBUG_UPLOAD_MSG:
                UploadGZippedData(deviceId, myText, ICAM_SERVER_NAME,
                                  WEBPAGE_UL_GZIPPED_TEXT, None)

            elif tokens[0] == "set-date-and-time":
                """
                IMPORTANT NOTE: We can also get the date and time by looking at
                    a (preferably small packet) received at
                    UploadGzippedStateAndFile.php (or even with
                    UploadGzippedText.php).
                """
                if SYMBIAN_OS:
                    e32.set_home_time(float(tokens[1])) # It expects float.
                elif RASPBIAN_OS:
                    """
                    See https://stackoverflow.com/questions/2193964/set-the-hardware-clock-in-python
                        and https://stackoverflow.com/questions/12081310/python-module-to-change-system-date-and-time
                    """
                    strDate = time.strftime("%m/%d/%Y %H:%M:%S", \
                                            time.localtime(float(tokens[1])))
                    os.system('date -s "%s"' % strDate)
                    DebugPrint("ExecuteCommands(): called date -s with " \
                                "param strDate = %s" % strDate)
                    """
                    From http://www.computerhope.com/unix/udate.htm:
                        date -s "11/20/2003 12:48:00"
                    """
                #!!!!TODO: implement for the other platforms, as well

            elif tokens[0] == "adjust-date-and-time-using-delta":
                """
                On S60 3rd+ edition we need WriteDeviceData capabilities (which
                    implies we have to sign with dev certificate) -
                    See http://discussion.forum.nokia.com/forum/showthread.php?119016-e32.set_home_time%20%20-fails-on-real-device#903719400565939968.

                # Inspired from
                    http://discussion.forum.nokia.com/forum/showthread.php?119016-e32.set_home_time%20%20-fails-on-real-device#32583428368845857418
                """

                """
                IMPORTANT: here we should really keep time.time(),
                    even for S60 2nd edition phones.
                """
                #myTime = GetTime() + int(tokens[1])
                myTime = time.time() + int(tokens[1])
                
                # myTime = FromStringToTime(tokens[1])
                # myTime = tokens[1]

                if SYMBIAN_OS:
                    """
                    From http://croozeus.com/Croozeus%20PyS60%20Tutorial8.htm
                        You can also set the phone's time (note that you need
                        WriteDeviceData capability) using:
                        e32.set_home_time(new_time) where new_time is in Unix
                        timestamp format (seconds since 01.01.1970, 00:00:00).

                    For example, e32.set_home_time(1134742324)
                        sets the time to 16.12.2005, 16:12:04
                    """
                    e32.set_home_time(myTime)
            elif tokens[0] == "set-burst-detection":
                burstModeIsStarted = int(tokens[1])

            elif tokens[0] == "set-motion-detection-number-hotspots":
                numHotspots = int(tokens[1])
                if numHotspots > MAX_NUM_HOTSPOTS:
                    return

            elif tokens[0] == "set-different-pixels-percentage-threshold":
                hotspotIndex = int(tokens[1])
                if hotspotIndex >= MAX_NUM_HOTSPOTS:
                    return
                differentPixelsPercentageThreshold[0] = \
                    int(tokens[2])

            elif tokens[0] == "set-motion-detection-hotspot":
                hotspotIndex = int(tokens[1])
                SetMotionDetectionHotspot(hotspotIndex, int(tokens[2]),
                        int(tokens[3]), int(tokens[4]), int(tokens[5]))

            elif tokens[0] == "set-store-locally-media":
                storeLocallyMedia = int(tokens[1])

            elif tokens[0] == "set-upload-media-to":
                uploadMediaToYouTube = int(tokens[1])
                uploadMediaToPicasa = int(tokens[2])
                useiCamServer = int(tokens[3])
                StoreLocalConfigInFile()

            elif tokens[0] == "set-google-username":
                googleUsername = tokens[1]
                StoreLocalConfigInFile()

            elif tokens[0] == "set-google-password-encrypted":
                googlePasswordEncrypted = tokens[1]
                StoreLocalConfigInFile()

            elif tokens[0] == "set-googleKeywords":
                googleKeywords = tokens[1]
                StoreLocalConfigInFile()

            elif tokens[0] == "set-googleMediaPrivate":
                googleMediaPrivate = int(tokens[1])
                StoreLocalConfigInFile()

            elif tokens[0] == "erase-oldest-files-and-messages":
                EraseOldestFilesAndMessages()

            elif tokens[0] == "set-mode-manager":
                modeManagerIsEnabled = int(tokens[1])

            elif tokens[0] == "set-dawn-time":
                # dawnTimeVec is hh:mm:ss
                timeTokens = tokens[1].split(":")
                dawnTimeVec = [int(timeTokens[0]),
                               int(timeTokens[1]), int(timeTokens[2])]

            elif tokens[0] == "set-dusk-time":
                # duskTimeVec is hh:mm:ss
                timeTokens = tokens[1].split(":")
                duskTimeVec = [int(timeTokens[0]),
                               int(timeTokens[1]), int(timeTokens[2])]

            elif tokens[0] == "exec-security-issues":
                """
                "exec-security-issues-no-stdouterr"):
                IMPORTANT TODO: !!!!check for dangerous commands
                See http://stackoverflow.com/questions/701802/how-do-i-execute-a-string-containing-python-code-in-python.
                "eval returns a value, but doesn't work for all commands.
                    exec works for all, but doesn't return a value.
                    Still trying to figure out a way around this".
                """
                myExecResult = ""

                # exec tokens[1]
                myCmd = cmdLine[len(tokens[0]) + 1:]
                """
                Note that we can put spaces in the commands following the
                    exec-... keyword.
                """
                exec myCmd

                myText = "myExecResult = %s" % str(myExecResult)
                # if MY_DEBUG_UPLOAD_MSG:
                UploadGZippedData(deviceId, myText, ICAM_SERVER_NAME,
                                  WEBPAGE_UL_GZIPPED_TEXT, None)
                DebugPrint("ExecuteCommands(): for command %s we got %s" % \
                        (myCmd, myText))
            """
            elif tokens[0] == "get-phone-model-and-number":
                PHONE_INFO_FILENAME = LOCAL_FOLDER + "/PhoneInfo.txt"
                fOutput = open(PHONE_INFO_FILENAME, "wb")
                fOutput.write("blabla")
                fOutput.close()
                UploadFile(PHONE_INFO_FILENAME, ICAM_SERVER_NAME,
                            WEBPAGE_UL_GZIPPED_FILE)
            #elif tokens[0] == "set-access-point":
            #elif tokens[0] == "get-phone-number":
            #http://docs.python.org/library/urllib.html
            """
        except:
            (exceptionType, exceptionValue, exceptionTraceback) = \
                sys.exc_info()

            myText = "Exception in ExecuteCommands(). Details: time = %s, " \
                        "free_ram = %d. exceptionTraceback = %s, " \
                        "exceptionType = %s, exceptionValue = %s." \
                        % (GetCurrentDateTimeStringNice(), GetFreeRAM(),
                           repr(traceback.format_tb(exceptionTraceback)),
                           str(exceptionType), str(exceptionValue))

            if MY_DEBUG_UPLOAD_MSG:
                UploadGZippedData(deviceId, myText, ICAM_SERVER_NAME,
                                  WEBPAGE_UL_GZIPPED_TEXT, None)

            DebugPrint(myText)
            #sys.stderr.write(myText + "\n")

            DebugPrintErrorTrace()
            # return False

    SetMenu()
    StoreState()

    return True


"""
Where to write the iCam commands in YouTube?
    - I googled to see if I can store an arbitrary file on YouTube - I didn't
        find anything. Only about storing on Gmail - but this is an unnecessary
        complication.

    - I use description in
        - playlist - a bit more difficult to read, but possible to write with
                Gdata
            - disadvantage - the playlist can be erased...
        - MAYBE in some video

    - other possible ideas to implement:
        - I can use "user profile" - very simple to read, seems impossible to
                    write with Gdata
            - we can keep locally on the phone (iCam.py) track of the fact we
                    downloaded command
            - https://code.google.com/apis/youtube/2.0/developers_guide_protocol.html#Profiles
                "The YouTube Data API allows you to retrieve user profiles."

              def GetYouTubeUserEntry(self, uri=None, username=None):
                Retrieve a YouTubeUserEntry.

            - to make certain fields in user profile private go to:
                - http://www.youtube.com/profile?user=googleUser#g/f - give edit on
                    profile and deselect the ones you don't want to make
                    public ;)
                - http://www.youtube.com/account_privacy

        - https://sites.google.com/site/googleUser/
            It seems there is no GData API for YT Messages
                http://www.youtube.com/inbox?folder=messages&action_message=1&authuser=0#inbox/1
                Note: I can send myself messages.

        - other APIs that can be explored:
            Running all tests in module gdata_tests.docs_test
            Running all tests in module gdata_tests.health_test
            Running all tests in module gdata_tests.spreadsheet_test
            Running all tests in module gdata_tests.blogger_test
            Running all tests in module gdata_tests.webmastertools_test

        - I probably cannot write commands in a file and pretend it's a video
                since YouTube will discard the video as invalid.
            - more complicated embeddings of commands in a real video are not
                very appropriate - for phone I guess it would be harder to
                decode them.
"""
def DownloadCommandsFromYouTube():
    try:
        DebugPrint("Entered DownloadCommandsFromYouTube(): " \
                    "uploadMediaToYouTube = %d." % uploadMediaToYouTube)

        if uploadMediaToYouTube == False:
            """
            DebugPrint("DownloadCommandsFromYouTube(): bailing out.")
            """
            return ""

        if youtubeClientAlreadyConnected == False:
            if gdataModulesImported == False:
                ImportGdataModules()
            connResult = ConnectToYouTubeGData()
            """
            If connResult == -1 then don't continue (most likely bad
                username/passwd).!!!!
            """

        playlistTitle = "iCam_cmd_" + deviceId
        playlistDescription = ""

        playlistToUse = None
        """
        We require the YouTube alias/nickname which can be different to the
            Google username!!!!!!!!
        #username='ender123')
        """
        # feed = youtubeClient.GetYouTubePlaylistFeed(username="MultiEnder123")

        feed = youtubeClient.GetYouTubePlaylistFeed()

        # Returns: A YouTubePlaylistFeed if successfully retrieved.
        # print "DownloadCommandsFromYouTube(): feed = %s" % str(feed)
        # print "feed.entry[0] =", feed.entry[0]
        for myEntry in feed.entry:
            myEntryTitle = myEntry.title.text
            # print "myEntryTitle = %s" % myEntryTitle

            # myEntry.id.text = http://gdata.youtube.com/feeds/api/users/MultiEnder123/playlists/3FD3773F7AC5DD1E
            # myEntry.id = <xml>...
            myEntryIdStr = myEntry.id.text.split("/")[-1]
            # print "  myEntryIdStr = %s" % myEntryIdStr

            if myEntryTitle == playlistTitle:
                DebugPrint("DownloadCommandsFromYouTube(): Feed matched " \
                            "myEntry = %s\n" % str(myEntry) + \
                    "DownloadCommandsFromYouTube(): myEntry.content = %s\n" % \
                        str(myEntry.content) + \
                    "DownloadCommandsFromYouTube(): " \
                        "myEntry.description = %s" % str(myEntry.description))

                # playlistDescription = myEntry.description.split("/")[-1]
                playlistDescription = \
                    str(myEntry.description).split(">")[-2].split("</")[0]

                DebugPrint("DownloadCommandsFromYouTube(): " \
                        "playlistDescription = %s" % str(playlistDescription))

                playlistToUse = myEntry
                # break

                patternNoCmd = "<ns0:description xmlns:ns0=\"" \
                                "http://gdata.youtube.com/schemas/2007\""

                if playlistDescription.find(patternNoCmd) != -1:
                    DebugPrint("DownloadCommandsFromYouTube(): This is not a " \
                                "command, just an empty description.")
                    playlistDescription = ""
                else:
                    DebugPrint("DownloadCommandsFromYouTube(): This is a " \
                                "real command.")

                if playlistDescription != "":
                    """
                    Erase the freshly downloaded commands from the YouTube
                        playlist (in order to not download and execute it
                        again).
                    """
                    youtubeClient.UpdatePlaylist(playlist_id=myEntryIdStr,
                            new_playlist_title=playlistTitle,
                            new_playlist_description="",
                            playlist_private=True, username="default")

                return playlistDescription

        """
        if playlistToUse is None:
            # Create the playlist if it was not found.
            # Returns: The YouTubePlaylistEntry if successfully posted.
            playlistToUse = youtubeClient.AddPlaylist(playlistTitle,
                                        playlistTitle, playlist_private=True)

        # It seems this info is not used!
        aVideoTitle = ""

        # It seems this info is not used!
        aVideoDescription = ""

        playlistURI = playlistToUse.feed_link[0].href

        # !!!!!!!!Maybe required
        #time.sleep(10)

        response = youtubeClient.AddPlaylistVideoEntryToPlaylist(playlistURI,
                        newVideoEntry.id.text.split('/')[-1],
                        aVideoTitle, aVideoDescription)
        """
    except:

        """
        newVideoEntry = youtubeClient.InsertVideoEntry(videoEntry,
                                        pathFileName)
        """
        (exceptionType, exceptionValue, exceptionTraceback) = sys.exc_info()

        errorStr = "Exception in DownloadCommandsFromYouTube() - details: " \
                    "exceptionTraceback = %s, exceptionType = %s, " \
                    "exceptionValue = %s. Bailing out..." % \
                    (repr(traceback.format_tb(exceptionTraceback)),
                       str(exceptionType), str(exceptionValue))

        DebugPrint(errorStr)
        DebugPrintErrorTrace()

        return ""


"""
We use this var to download command from YouTube only once every X (=5)
    commands from iCam server.
"""
MAX_NUM_COMMANDS_FILES = 5
def DownloadCommands_real():
    global deviceId
    global ICAM_SERVER_NAME
    global accessPointRetryConnect, accessPointName
    global uploadMediaToYouTube
    global downloadCommandsCounter

    DebugPrint("Entered DownloadCommands_real(): accessPointName = %s, " \
                "downloadCommandsCounter = %d." % (accessPointName, \
                                                    downloadCommandsCounter))

    try:
        """
        We allow more than one commands file because, in case we have multiple
          commands to issue, it is possible that iCam will crash (at least the
          S60 version), while executing one of the commands. Therefore,
          we allow files cmd.txt, cmd.txt.1, ..., cmd.txt.5 such that if iCam
          restarts it will continue processing the remaining cmd.txt* files.
        """
        executedLocalCommands = False
        for index in range(MAX_NUM_COMMANDS_FILES):
            pathFileName = LOCAL_FOLDER + "/" + COMMANDS_FILENAME

            if index != 0:
                pathFileName = pathFileName + ".%d" % index

            if os.path.isfile(pathFileName):
                try:
                    fInput = open(pathFileName, "rb") #"rt"????
                    myCommands = fInput.read()
                    fInput.close()

                    # Should I delete pathFileName after
                    #   ExecuteCommands(myCommands)? !!!!
                    os.unlink(pathFileName)
                except:
                    DebugPrintErrorTrace()

                #UpdateDownloadCommandsCounter()
                #return ExecuteCommands(myCommands)
                executedLocalCommands = True
                res = ExecuteCommands(myCommands)
        if executedLocalCommands == True:
            return

        if NoInternetConnection():
            DebugPrint("DownloadCommands(): Not downloading commands since " \
                        "there is no Internet connection.")

            #UpdateDownloadCommandsCounter()
            return False
    except:
        DebugPrintErrorTrace()

    try:
        if iOS_PYOBJC:
            DebugPrint("DownloadCommands(): Before urllib.urlopen().")
            # urllib.urlopen() seems to crash the application...

        if uploadMediaToYouTube:
            if downloadCommandsCounter == 0:
                myCommands = DownloadCommandsFromYouTube()
                # GetYouTubeUserProfile()

                #UpdateDownloadCommandsCounter()

                if myCommands != "":
                    return ExecuteCommands(myCommands)

        #UpdateDownloadCommandsCounter()

        #if USE_ICAM_SERVER:
        if useiCamServer > 0:
            DebugPrint("DownloadCommands(): Now trying to download command " \
                        "from iCam server.")

            myCommandsCompressed = urllib.urlopen("http://" + ICAM_SERVER_NAME +
                    WEBPAGE_DL_COMMAND_FILE + "?deviceId=" + deviceId).read()
            myCommands = myCommandsCompressed.decode("zlib")

            if iOS_PYOBJC:
                DebugPrint("DownloadCommands(): After urllib.urlopen().")

            #UpdateDownloadCommandsCounter()
            return ExecuteCommands(myCommands)
    # except IOError: print "DownloadCommands IOError"
    except:
        (exceptionType, exceptionValue, exceptionTraceback) = sys.exc_info()
        myText = "Exception in DownloadCommands. Details: time = %s, " \
                    "free_ram = %d. exceptionTraceback = %s, " \
                    "exceptionType = %s, exceptionValue = %s." % \
                    (GetCurrentDateTimeStringNice(), GetFreeRAM(),
                       repr(traceback.format_tb(exceptionTraceback)),
                       str(exceptionType), str(exceptionValue))

        if MY_DEBUG_UPLOAD_MSG:
            UploadGZippedData(deviceId, myText, ICAM_SERVER_NAME,
                              WEBPAGE_UL_GZIPPED_TEXT, None)

        #sys.stderr.write(myText + "\n")
        DebugPrint(myText)

        DebugPrintErrorTrace()

        #UpdateDownloadCommandsCounter()
        return False

    #UploadText("Read command file", ICAM_SERVER_NAME, WEBPAGE_UL_GZIPPED_TEXT)
    #return res


downloadCommandsCounter = 0
"""
DOWNLOAD_COMMANDS_COUNTER_MAX is the number of iCam server command downloads
    before we attempt to download a command from YouTube
    (which we want to do less often in the ~idea it's more data intensive).
Has to be > 1 since I use downloadCommandsCounter for sync with the waiting
    loop in MainLog().

"""
DOWNLOAD_COMMANDS_COUNTER_MAX = 5

"""
!!!!Although we give hasDownloadedNewCmd = DownloadCommands() and we do not use
    hasDownloadedNewCmd anywhere at all,
We should maybe return result from the function, as it was doing the original
    DownloadCommands().
"""
def DownloadCommands():
    def UpdateDownloadCommandsCounter():
        global downloadCommandsCounter
        downloadCommandsCounter += 1
        if downloadCommandsCounter == DOWNLOAD_COMMANDS_COUNTER_MAX:
            downloadCommandsCounter = 0

    DebugPrint("Entered DownloadCommands().")

    try:
        res = DownloadCommands_real()
    except:
        DebugPrintErrorTrace()

    UpdateDownloadCommandsCounter()

    DebugPrint("Exiting DownloadCommands().")

    return res

    # thread.start_new_thread(ReactiveLoop_real, ())
    # MyThreadStart(DownloadCommands_real)


"""
In order for the viewfinder to correspond to the zoom level, we must take a
    picture (without saving it), close and open the viewfinder.
These steps are necessary because of the way the functions are currently
    defined in PyS60, and have a slight impact on performance.
Future releases of PyS60 may have optimized functions.

# Take the picture with cameraId = 0.
#pic = camera.take_photo('RGB', cameraPhotoSizes[1], digitalZoom, 'none',
#                        'auto', 'auto', 0)
#camera.stop_finder()
#camera.start_finder(ViewFinderCallback, backlight_on = 1, size = (240,180))
"""
def GetTextForState(cameraId):
    resText = ""
    try:
        resText = "Free space in bytes on drives: C = %d, D = %d, E = %d. " \
                    % (GetFreeDriveSpace("C:"), GetFreeDriveSpace("D:"),
                       GetFreeDriveSpace("E:"))

        resText += "free_ram = %d. GSM network signal strength = %d [%s]. " \
                    "Battery = %d, charger_status = %d. " \
                    "Pause interval (pauseInterval) = %d. " \
                    "Camera modes = (%d, %d). " \
                    % (
                        GetFreeRAM(),
                        signalStrength, signalUnits,
                        GetBatteryLevelPercentage(), GetChargerStatus(),
                        pauseInterval,
                        cameraMode[0], cameraMode[1]
                    )

        resText += "Resolution (photoResolutionIndex) = %d. " \
                    "photoModeIndex = %d. digitalZoom = %d. " \
                    "photoQuality = %d. exposureIndex[0] = %d. " \
                    % (photoResolutionIndex, photoModeIndex[cameraId],
                       digitalZoom, photoQuality, exposureIndex[0])

        resText += "whiteBalanceIndex[0] = %d. exposureIndex[1] = %d. " \
                    "whiteBalanceIndex[1] = %d. flashIndex = %d. " \
                    "audioRecordDuration = %d. " \
                    % (whiteBalanceIndex[0], exposureIndex[1],
                       whiteBalanceIndex[1], flashIndex, audioRecordDuration)

        resText += "rotateDegreesImage = %d. mobileCountryCode = %d. " \
                    "mobileNetworkCode = %d. locationAreaCode = %d. " \
                    "cellId = %d. " % \
                    (rotateDegreesImage, mobileCountryCode,
                       mobileNetworkCode, locationAreaCode, cellId)

        if SYMBIAN_OS:
            """
            It seems miso's heap and/or stack functions give exception:
                "SymbianError: [Errno -5] KErrNotSupported".
            """
            if (S60_EDITION[0] >= 3) and misoIsImported:
            # if misoIsImported:
                resText += "miso: num_alloc_heap_cells() = %d, " \
                            "num_free_heap_cells() = %d, " \
                            "alloc_heap_cells_size() = %d, " \
                            "heap_total_avail() = %d, " \
                            "heap_biggest_avail() = %d, " \
                            "heap_base_address() = %d, stack_info() = %s.\n" \
                            % (
                                miso.num_alloc_heap_cells(),
                                miso.num_free_heap_cells(),
                                miso.alloc_heap_cells_size(),
                                miso.heap_total_avail(),
                                miso.heap_biggest_avail(),
                                miso.heap_base_address(),
                                str(miso.stack_info())
                            )
    except:
        DebugPrint("Exception in GetTextForState(cameraId = %d)..." % cameraId)
        DebugPrintErrorTrace()

    return resText


# Upload file from local filesystem to the server.
def UploadFile(pathFileName, inetServerAddress, pageOnServer):
    global deviceId

    DebugPrint("Entered UploadFile().")

    # See http://docs.python.org/library/struct.html
    """
    # (32 bits long??) integers, in little endian format.
    IMPORTANT NOTE: we use "<" to specify ALSO no alignment -
        see http://docs.python.org/library/struct.html
    """
    myFormat = "<256s"

    dataHeader = struct.pack(myFormat, pathFileName)
    sizeDataHeader = len(dataHeader)
    """
    IMPORTANT NOTE: we use "<" to specify little endian and no alignment - see
        http://docs.python.org/library/struct.html .
    """
    dataHeader = struct.pack("<i", sizeDataHeader) + dataHeader

    try:
        """
        Read the binary file from disk in chunks, and compress with Zlib using
            stream aware compress objects - see
            http://docs.python.org/library/zlib.html and
            http://stackoverflow.com/questions/2423866/python-decompressing-gzip-chunk-by-chunk/2424549#2424549
            for details.
        """
        # myData = ""
        myBufferSize = 64 * 1024

        # 1 = lowest compression for zlib.compressobj() .
        zlibCompressObject = zlib.compressobj(1)

        # myBuffer = struct.pack("100s", deviceId) + dataHeader
        myBuffer = AddPacketHeader(dataHeader)
        myData = zlibCompressObject.compress(myBuffer)

        fInput = open(pathFileName, "rb")

        while True:
            myBuffer = fInput.read(myBufferSize)
            if len(myBuffer) == 0:
                break
            myData += zlibCompressObject.compress(myBuffer)

        fInput.close()

        myData += zlibCompressObject.flush()

        """
        fInput = open(pathFileName, "rb")
        myData = fInput.read()
        fInput.close()
        """
    except:
        # myData = dataHeader + myData
        (exceptionType, exceptionValue, exceptionTraceback) = sys.exc_info()

        if MY_DEBUG_UPLOAD_MSG:
            UploadGZippedData(deviceId,
                "Exception in UploadFile with pathFileName = %s - details: " \
                "free_ram = %d. %s. Bailing out..." % (pathFileName, 
                    GetFreeRAM(),
                    repr(traceback.format_tb(exceptionTraceback))),
                    ICAM_SERVER_NAME, WEBPAGE_UL_GZIPPED_TEXT,
                    None)

        DebugPrint("Exception in UploadFile with pathFileName = %s. " \
                    "Bailing out..." % pathFileName)
        DebugPrintErrorTrace()

        return

    try:
        """
        This returns the basename of the file - I think os.path.basename() is
            not supported on PyS60.
        """
        fileName = os.path.split(pathFileName)[1]
    except:
        DebugPrintErrorTrace()
    return UploadBinaryData(myData, inetServerAddress, pageOnServer,
                            fileName)

    #return UploadGZippedData(deviceId, myData, inetServerAddress,
    #    pageOnServer, fileName)


def TestPhoneCall():
    if ANDROID_OS:
        try:
            time.sleep(1.0)  # 10.0)

            DebugPrint("Before myDroid.phoneCall()")

            # Does not work on Prestigio.
            myDroid.phoneCall("tel:" + ALARM_PHONE_NUMBER)

            """
            Does not work on Prestigio. Gives exception:
                "java.lang.NullPointerException".
            """
            # myDroid.phoneCall(ALARM_PHONE_NUMBER)

            DebugPrint("After myDroid.phoneCall()")
        except:
            DebugPrintErrorTrace()


lastResultGetLatestPhoto = None


"""
!!!!TODO: rename LogToYouTubePlaylist() -> ConnectToYouTubeLogPlaylist()
"""
def LogToYouTubePlaylist():
    global youtubeClient, youtubeClientAlreadyConnected
    global YOUTUBE_TEST_CLIENT_ID, googleUsername, youtubeDeveloperKey
    global uploadMediaToYouTube
    global deviceId
    global btAddrTable

    DebugPrint("Entered LogToYouTubePlaylist() at %s." % \
                                GetCurrentDateTimeStringWithMilliseconds())

    """
    if uploadMediaToYouTube == 0:
        uploadMediaToYouTube = 1
    """

    if youtubeClientAlreadyConnected == False:
        if gdataModulesImported == False:
            ImportGdataModules()

        # If connResult == -1 then don't continue
        #    (most likely bad username/passwd).!!!!
        connResult = ConnectToYouTubeGData()

    try:
        playlistTitle = "iCam_log_" + deviceId  #354525040419119

        # Create the playlist if it doesn't exist.
        if True:
            playlistDescription = playlistTitle

            playlistToUse = None

            """
            !!!!We require the YouTube alias/nickname which can be different
                to the Google username!!!!!!!!
            """
            #feed = youtubeClient.GetYouTubePlaylistFeed(username =
            #    "MultiEnder123") #username='ender123')
            feed = youtubeClient.GetYouTubePlaylistFeed()

            # Returns: A YouTubePlaylistFeed if successfully retrieved.
            # print "feed =", feed
            # print "feed.entry[0] =", feed.entry[0]
            for myEntry in feed.entry:
                myEntryTitle = myEntry.title.text

                # print "myEntryTitle = %s" % myEntryTitle
                #myEntry.id.text =
                #    http://gdata.youtube.com/feeds/api/users/MultiEnder123/playlists/3FD3773F7AC5DD1E

                # myEntry.id = <xml>...
                myEntryIdStr = myEntry.id.text.split("/")[-1]

                #print "  myEntryIdStr = %s" % myEntryIdStr
                if playlistTitle == myEntryTitle:
                    playlistToUse = myEntry
                    break

            if playlistToUse is None:
                # Create the playlist if it was not found
                playlistToUse = \
                    youtubeClient.AddPlaylist(playlistTitle,
                        playlistTitle, playlist_private=False)
                # Returns: The YouTubePlaylistEntry if successfully posted.

        """
        Note: YouTube has description playlists of at max 5000 characters.
            Picasa has album descriptions of max 1000 characters.
        """
        playlistDescription = ""
        newPlaylistDescription = \
            "batteryLevel = %d; chargerStatus = %d; " % \
            (GetBatteryLevelPercentage(), GetChargerStatus())
        newPlaylistDescription += \
                    "btAddrTable = %s\n" % (str(btAddrTable))

        DebugPrint("LogToYouTubePlaylist(): newPlaylistDescription = %s." % \
                                            newPlaylistDescription)
        """
        newPlaylistDescription += \
                    "btMsgMostRecentTime = %s, " \
                    "btAddrTable = %s" % \
                    (str(btMsgMostRecentTime), str(btAddrTable)))
        """

        playlistToUse = None

        """
        !!!!We require the YouTube alias/nickname which can be different to
            the Google username!!!!
        """

        #feed = youtubeClient.GetYouTubePlaylistFeed(username =
        #    "MultiEnder123") #username='ender123')

        feed = youtubeClient.GetYouTubePlaylistFeed()

        # Find the YouTube playlist

        # Returns: A YouTubePlaylistFeed if successfully retrieved.
        # print "DownloadCommandsFromYouTube(): feed = %s" % str(feed)
        # print "feed.entry[0] =", feed.entry[0]
        for myEntry in feed.entry:
            myEntryTitle = myEntry.title.text

            # print "myEntryTitle = %s" % myEntryTitle
            # myEntry.id.text =
            #    http://gdata.youtube.com/feeds/api/users/MultiEnder123/playlists/3FD3773F7AC5DD1E

            # myEntry.id = <xml>...
            myEntryIdStr = myEntry.id.text.split("/")[-1]

            # print "  myEntryIdStr = %s" % myEntryIdStr
            if myEntryTitle == playlistTitle:
                DebugPrint("LogToYouTubePlaylist(): " \
                            "Feed matched myEntry = %s\n" % str(myEntry) + \
                    "LogToYouTubePlaylist(): " \
                            "myEntry.content = %s\n" % str(myEntry.content) + \
                    "LogToYouTubePlaylist(): " \
                        "myEntry.description = %s" % str(myEntry.description))

                # playlistDescription = myEntry.description.split("/")[-1]
                playlistDescription = str(myEntry.description).\
                            split(">")[-2].split("</")[0]

                DebugPrint("LogToYouTubePlaylist(): " \
                        "playlistDescription = %s" % str(playlistDescription))

                playlistToUse = myEntry

                break

        if playlistToUse is None:
            # The YouTube playlist was not found --> we create one
            DebugPrint("LogToYouTubePlaylist(): Couldn't find " \
                        "YouTube playlist %s. Creating it." % playlistTitle)

            # Create the playlist if it was not found
            playlistToUse = youtubeClient.AddPlaylist(playlistTitle,
                    newPlaylistDescription, playlist_private=False)
            # Returns: The YouTubePlaylistEntry if successfully posted.

            myEntryIdStr = playlistToUse.id.text.split("/")[-1]
        else:
            if len(newPlaylistDescription) + len(playlistDescription) < 5000:
                playlistDescription += newPlaylistDescription
            else:
                playlistDescription = newPlaylistDescription

            # if playlistDescription != "":
            youtubeClient.UpdatePlaylist(playlist_id=myEntryIdStr,
                    new_playlist_title=playlistTitle,
                    new_playlist_description=playlistDescription,
                    playlist_private=True, username="default")

        # return playlistDescription
    except:
        #newVideoEntry = youtubeClient.InsertVideoEntry(videoEntry,
        #    pathFileName)
        DebugPrintErrorTrace()

    DebugPrint("Exiting LogToYouTubePlaylist() at %s." % \
                            GetCurrentDateTimeStringWithMilliseconds())


def SendAlarmMessageToYouTubePlaylist(message):
    global youtubeClient, youtubeClientAlreadyConnected
    global YOUTUBE_TEST_CLIENT_ID, googleUsername, youtubeDeveloperKey
    global uploadMediaToYouTube
    global deviceId

    DebugPrint("Entered SendAlarmMessageToYouTubePlaylist() at %s." % \
                                GetCurrentDateTimeStringWithMilliseconds())

    if uploadMediaToYouTube == 0:
        uploadMediaToYouTube = 1

    if youtubeClientAlreadyConnected == False:
        if gdataModulesImported == False:
            ImportGdataModules()

        # If connResult == -1 then don't continue
        #    (most likely bad username/passwd).!!!!
        connResult = ConnectToYouTubeGData()

    try:
        playlistTitle = "iCam_alarm_" + deviceId  #354525040419119

        # Create the playlist if it doesn't exist.
        if False:
            playlistDescription = playlistTitle

            playlistToUse = None

            """
            !!!!We require the YouTube alias/nickname which can be different
                to the Google username!!!!!!!!
            """
            #feed = youtubeClient.GetYouTubePlaylistFeed(username =
            #    "MultiEnder123") #username='ender123')
            feed = youtubeClient.GetYouTubePlaylistFeed()

            # Returns: A YouTubePlaylistFeed if successfully retrieved.
            # print "feed =", feed
            # print "feed.entry[0] =", feed.entry[0]
            for myEntry in feed.entry:
                myEntryTitle = myEntry.title.text

                # print "myEntryTitle = %s" % myEntryTitle
                #myEntry.id.text =
                #    http://gdata.youtube.com/feeds/api/users/MultiEnder123/playlists/3FD3773F7AC5DD1E

                # myEntry.id = <xml>...
                myEntryIdStr = myEntry.id.text.split("/")[-1]

                #print "  myEntryIdStr = %s" % myEntryIdStr
                if playlistTitle == myEntryTitle:
                    playlistToUse = myEntry
                    break

            if playlistToUse is None:
                # Create the playlist if it was not found
                playlistToUse = \
                    youtubeClient.AddPlaylist(playlistTitle,
                        playlistTitle, playlist_private=False)
                # Returns: The YouTubePlaylistEntry if successfully posted.

        """
        Note: YouTube has description playlists of at max 5000 characters.
            Picasa has album descriptions of max 1000 characters.
        """
        playlistDescription = ""
        newPlaylistDescription = \
            "Alarm... motion degree... audio degree... %s." % message

        playlistToUse = None

        """
        !!!!We require the YouTube alias/nickname which can be different to
            the Google username!!!!
        """

        #feed = youtubeClient.GetYouTubePlaylistFeed(username =
        #    "MultiEnder123") #username='ender123')

        feed = youtubeClient.GetYouTubePlaylistFeed()

        # Returns: A YouTubePlaylistFeed if successfully retrieved.
        # print "DownloadCommandsFromYouTube(): feed = %s" % str(feed)
        # print "feed.entry[0] =", feed.entry[0]
        for myEntry in feed.entry:
            myEntryTitle = myEntry.title.text

            # print "myEntryTitle = %s" % myEntryTitle
            # myEntry.id.text =
            #    http://gdata.youtube.com/feeds/api/users/MultiEnder123/playlists/3FD3773F7AC5DD1E

            # myEntry.id = <xml>...
            myEntryIdStr = myEntry.id.text.split("/")[-1]

            # print "  myEntryIdStr = %s" % myEntryIdStr
            if myEntryTitle == playlistTitle:
                DebugPrint("SendAlarmMessageToYouTubePlaylist(): " \
                            "Feed matched myEntry = %s\n" % str(myEntry) + \
                    "SendAlarmMessageToYouTubePlaylist(): " \
                            "myEntry.content = %s\n" % str(myEntry.content) + \
                    "SendAlarmMessageToYouTubePlaylist(): " \
                        "myEntry.description = %s" % str(myEntry.description))

                # playlistDescription = myEntry.description.split("/")[-1]
                playlistDescription = str(myEntry.description).\
                            split(">")[-2].split("</")[0]

                DebugPrint("SendAlarmMessageToYouTubePlaylist(): " \
                        "playlistDescription = %s" % str(playlistDescription))

                playlistToUse = myEntry

                break

                """
                patternNoCmd = "<ns0:description xmlns:ns0=" \
                                "\"http://gdata.youtube.com/schemas/2007\""

                if playlistDescription.find(patternNoCmd) != -1:
                    DebugPrint("SendAlarmMessageToYouTubePlaylist(): " \
                        "This is not a command, just an empty description.")

                    playlistDescription = ""
                else:
                    DebugPrint("SendAlarmMessageToYouTubePlaylist(): " \
                                "This is a real command.")
                """

        if playlistToUse is None:
            DebugPrint("SendAlarmMessageToYouTubePlaylist(): Couldn't find " \
                        "YouTube playlist %s. Creating it." % playlistTitle)

            # Create the playlist if it was not found
            playlistToUse = youtubeClient.AddPlaylist(playlistTitle,
                    newPlaylistDescription, playlist_private=False)
            # Returns: The YouTubePlaylistEntry if successfully posted.

            myEntryIdStr = playlistToUse.id.text.split("/")[-1]
        else:
            if len(newPlaylistDescription) + len(playlistDescription) < 5000:
                playlistDescription += newPlaylistDescription
            else:
                playlistDescription = newPlaylistDescription

            # if playlistDescription != "":
            youtubeClient.UpdatePlaylist(playlist_id=myEntryIdStr,
                    new_playlist_title=playlistTitle,
                    new_playlist_description=playlistDescription,
                    playlist_private=True, username="default")

        # return playlistDescription

        if False:
            # It seems this info is not used!
            aVideoTitle = ""

            # It seems this info is not used!
            aVideoDescription = ""
            playlistURI = playlistToUse.feed_link[0].href

            # time.sleep(10) #!!!!!!!!Maybe required

            response = youtubeClient.AddPlaylistVideoEntryToPlaylist(
                            playlistURI, "!!!!", #newVideoEntry.id.text.split("/")[-1],
                            aVideoTitle, aVideoDescription)
    except:
        #newVideoEntry = youtubeClient.InsertVideoEntry(videoEntry,
        #    pathFileName)
        DebugPrintErrorTrace()

    DebugPrint("Exiting SendAlarmMessageToYouTubePlaylist() at %s." % \
                            GetCurrentDateTimeStringWithMilliseconds())


"""
def CreateAlarmPicasaAlbum():
    if picasaClientAlreadyConnected == False:
        if gdataModulesImported == False:
            ImportGdataModules()
        ConnectToPicasaGData()

    try:
        albumTitle = deviceId + ": " + time.strftime("%Y-%m-%d", crtTime) + \
                     (", %d" % cameraId)

        iCamAlbumFound = False

        # From
        #  https://code.google.com/apis/picasaweb/docs/1.0/developers_guide_python.html#ListAlbums
        #  (less important: https://code.google.com/apis/picasaweb/docs/2.0/developers_guide_protocol.html#ListAlbums)

        # albumsFeed = picasaClient.GetUserFeed(kind = "album", user = "ender123")
        albumsFeed = picasaClient.GetUserFeed(kind = "album")

        #    # IMPORTANT NOTE: it does not print the album list - I believe
        #    #   BECAUSE THE tostring() METHOD DOESN'T DUMP THE ALBUMS LIST :))
        #DebugPrint("albumsFeed = %s" % str(albumsFeed))
        for album in albumsFeed.entry:
            DebugPrint("Title: %s, number of photos: %s, id: %s" % \
                        (album.title.text, album.numphotos.text,
                            album.gphoto_id.text))

            #picasaClient.Delete(album)

            if album.title.text == albumTitle:
                iCamAlbumFound = True
                break

        if iCamAlbumFound == True:
            #print "Found"
            iCamAlbum = album
        else:
            #print "Not found"

            #!!!!IMPORTANT: Unfortunately this private argument gets translated
            #   in Picasa in the attribute Visibility - "Anyone with the link".
            # Currently to fix this, the user has to go on Picasa and edit the
            #     attribute manually.
            # Find a programatic solution...!!!!

            iCamAlbum = picasaClient.InsertAlbum(title = albumTitle,
                                                 summary = "iCam photo album.",
                                                 location = "Bucharest",
                                                 access = "private")
    except:
        DebugPrintErrorTrace()
"""


def GetLatestPhoto(anExtension=".jpg"):
    global lastResultGetLatestPhoto

    try:
        # For Android, GetCurrentDateTime() .
        crtTime = time.localtime()

        crtLOCAL_FOLDER_MEDIA_FILES = LOCAL_FOLDER_MEDIA_FILES + "/" + \
            "%04d_%02d_%02d_%02d" % (crtTime.tm_year, crtTime.tm_mon,
                crtTime.tm_mday, crtTime.tm_hour)

        """
        DebugPrint("crtLOCAL_FOLDER_MEDIA_FILES = %s" % \
                                    crtLOCAL_FOLDER_MEDIA_FILES)
        """

        #crtTime2 = GetTime()

        # See http://discussion.forum.nokia.com/forum/showthread.php?116978-What-is-the-time-granularity-in-Pys60 .
        #numMilliseconds = (crtTime2 - int(crtTime2)) * 1000
        #crtTime.tm_year, crtTime.tm_mon, crtTime.tm_mday, crtTime.tm_hour,
        #   crtTime.tm_min, crtTime.tm_sec, numMilliseconds

        folderContent = os.listdir(crtLOCAL_FOLDER_MEDIA_FILES)

        """
        Put in sortedFolderContent only the filenames for the folderContent
            that don't have the extension anExtension.
        """
        sortedFolderContent = []
        for elem in folderContent:
            if str.lower(elem).find(anExtension) != -1:
                # print "%s doesn't contain anExtension" % elem
                # folderContent = folderContent.remove(elem)
                # folderContent.remove(elem)
                sortedFolderContent.append(elem)

        """
        Use reverse = False to send first the oldest ones (like this you send
            in chronological order). Use reverse = True for sending first the
            most recent ones.
        """
        # sortedFolderContent = sorted(folderContent, reverse = False)
        """
        sort() without parameters is the ONLY one that works in
            Python 2.2.
        (Info on sort at http://wiki.python.org/moin/HowTo/Sorting/.)
        """
        sortedFolderContent.sort()
        # sortedFolderContent = folderContent

        # """
        """
        DebugPrint("VideoRecordAndUpload(): sortedFolderContent = %s." % \
                                                        sortedFolderContent)
        """
        DebugPrint("GetLatestPhoto(): sortedFolderContent = %s\n" % \
                str(sortedFolderContent) + \
            "GetLatestPhoto(): " \
                "sortedFolderContent[len(sortedFolderContent) - 1] = %s" % \
                sortedFolderContent[len(sortedFolderContent) - 1])
        # """

        #if len(sortedFolderContent) < 1:
        if len(sortedFolderContent) < 2:
        #if sortedFolderContent != []:
            return None

        if False:
            #actualFileName = sortedFolderContent[len(sortedFolderContent) - 1]
            actualFileName = sortedFolderContent[len(sortedFolderContent) - 1]
            photoPathFileName = crtLOCAL_FOLDER_MEDIA_FILES + "/" + actualFileName

            resultGetLatestPhoto = (actualFileName, photoPathFileName)

            if lastResultGetLatestPhoto == resultGetLatestPhoto:
                return None
            else:
                lastResultGetLatestPhoto = resultGetLatestPhoto

                """
                # We have a new photo - so we call the alarm phone.

                #From https://code.google.com/p/android-scripting/wiki/ApiReference#phoneCall
                    phoneCall(String uri)

                #From https://code.google.com/p/android-scripting/wiki/ApiReference#smsSend
                    smsSend(String destinationAddress:
                                typically a phone number, String text)
                    Sends an SMS.

                if ANDROID_OS:
                    myDroid.phoneCall("tel:" + ALARM_PHONE_NUMBER)
                    #myDroid.smsSend("tel:+01234567890", "Max 160 chars I guess")
                """
                SendAlarmMessageToYouTubePlaylist(actualFileName)

                if False:
                    if GetCurrentDateTimeStringWithMilliseconds().startswith(
                            "2012_09_02_17"):
                        myDroid.smsSend(ALARM_SMS_PHONE_NUMBER,
                                        "Houston, we've got ack.")

                return resultGetLatestPhoto
    except:
        DebugPrintErrorTrace()



def TakePhotoAndUpload_S60(cameraId, photoFileName, \
                                        photoPathFileName):
    global accessPointName
    global LOCAL_FOLDER_MEDIA_FILES, bluetoothMode
    global flashStr, exposureStr, whiteBalanceStr
    global orientationForThisPhoneModel
    global digitalZoom, flashIndex, exposureIndex, whiteBalanceIndex


    if (cameraId == 0) and (orientationForThisPhoneModel == "landscape"):
        # (phoneModel in ["Nokia6120", "NokiaN95", "NokiaN82"])):
        """
        We give reset_inactivity() in order to turn the display on in
            order for the cellphone to capture at maximum (angular) view,
            at the maximum resolution.
        """
        e32.reset_inactivity()
        SetUIOrientation(cameraId, True)
        e32.ao_sleep(1)

        """
        Unfortunately it doesn't help: even if the viewfinder controls well
            the exposure, etc, the photo taken is still with its own
            settings, independent of the viewfinder.

        On models with shutter like N95, N82, etc, it is better to start
            the VF, in order to avoid taking photos over-exposed, etc - it
            seems the viewfinder allows to control automatically the photo
            params.
        """
        if startViewfinderBeforeTakingPhoto:
        #       and (phoneModel in ["NokiaN95", "NokiaN82"])):
        # if startViewfinderBeforeTakingPhoto or (not startAutomatically):
            StartViewFinderForCamera(cameraId, True, True)
            # With the backlight on (param True).
            e32.ao_sleep(3)

        # e32.ao_yield()
        e32.reset_inactivity()

    """
    UploadText("Taking photo: cameraId = %d, resolution = %d x %d, " \
                "zoom = %d. JPEG quality = %d." \
                % (cameraId, photoResolutionStr[photoResolutionIndex][1][0],
                    photoResolutionStr[photoResolutionIndex][1][1],
                    digitalZoom, photoQuality), ICAM_SERVER_NAME,
                    WEBPAGE_UL_GZIPPED_TEXT)
    """

    """
    From S60 Module Reference, Release 2.0.0 final:
                    take_photo([mode,
                                size,
                                zoom,
                                flash,
                                exposure,
                                white balance,
                                position])
    """
    try:
        """
        camera.take_photo('RGB',
                photoResolutionStr[photoResolutionIndex][1], digitalZoom,
                'none', 'auto', 'auto', cameraId)

        pic = camera.take_photo('RGB', localPhotoResolution[cameraId],
                digitalZoom, 'none', 'auto', 'auto', cameraId)
        """

        # Take the picture with cameraId.
        pic = camera.take_photo(
                    photoModeStr[photoModeIndex[cameraId]][1],
                    localPhotoResolution[cameraId],
                    digitalZoom,
                    flashStr[flashIndex][1],
                    exposureStr[exposureIndex[cameraId]][1],
                    whiteBalanceStr[whiteBalanceIndex[cameraId]][1],
                    cameraId
            )

        if phoneModel in ["NokiaN95", "NokiaN82"]:
            """
            # In order to close the shutter immediately after taking
            #   the photo.

            camera.release()
            #camera._my_camera = camera._camera.Camera(cameraId)
            camera.UseCamera(cameraId)
            """
            GeneralUseCameraS60(cameraId)
    except:
        pic = None

        (exceptionType, exceptionValue, exceptionTraceback) = \
                                                    sys.exc_info()

        # traceback.print_exc()
        errorStr = "TakePhotoAndUpload_S60(%d): camera.take_photo for " \
                    "photoFileName = %s, localPhotoResolution = %s, " \
                    "free_ram = %d returned exception %s. Bailing out..." % \
                    (cameraId, photoFileName,
                       localPhotoResolution[cameraId], GetFreeRAM(),
                       repr(traceback.format_tb(exceptionTraceback)))

        """
        UploadGZippedData(deviceId,
                            "Exception in TakePhotoAndUpload(%d)" \
                            " with photoFileName = %s: camera.take_photo" \
                            " - details: localPhotoResolution = %s, " \
                            "free_ram = %d. %s. Bailing out...\n" \
                            % (cameraId, photoFileName,
                                localPhotoResolution[cameraId],
                                GetFreeRAM(),
                            repr(traceback.format_tb(exceptionTraceback))),
                            ICAM_SERVER_NAME, WEBPAGE_UL_GZIPPED_TEXT, None)
        """
        if MY_DEBUG_UPLOAD_MSG:
            UploadGZippedData(deviceId, errorStr, ICAM_SERVER_NAME,
                              WEBPAGE_UL_GZIPPED_TEXT, None)

        DebugPrint(errorStr)

        DebugPrintErrorTrace()

        if MY_DEBUG_STDERR_2:
            sys.stderr.write("    " + errorStr + "\n")
            sys.stderr.flush()
        return

    """
    save(filename[, callback=None, format=None, quality=75, bpp=24,
            compression='default' ])
    """
    try:
        # Save the photo (as JPEG) with photoQuality
        #pic.save(photoPathFileName, quality = photoQuality)

        if photoModeStr[photoModeIndex[cameraId]][1] == "JPEG_Exif":
            """
            In "JPEG_Exif" mode take_photo returns the JPEG file to be
                saved, and not a photo, so it needs to be saved directly
                to the disk and then reloaded in a graphics.Image object.
            """
            fOutput = open(photoPathFileName, "wb")
            fOutput.write(pic)
            # fOutput.flush()
            fOutput.close()
            # return

            DebugPrint("Saved JPEG data object received directly from " \
                        "the hardware, without calling pic.save().")

            if not MODE_FOR_PHONE_WITH_LITTLE_RAM_AND_UNRELIABLE_MEM_CARD:
                """
                Requires backslashes, otherwise graphics.Image.open
                    gives exception: SymbianError: [Errno -28] KErrBadName
                """
                photoPathFileNameWithBackslashes = \
                    photoPathFileName.replace("/", "\\")

                """
                We assign None to reassign it, IF necessary, the
                    Image.open(photoPathFileNameWithBackslashes).
                """
                pic = None
        else:
            # Assuming RGB (RGB24)
            """
            save(filename[, callback=None, format=None, quality=75, bpp=24,
                    compression='default' ])
            """

            """
            if storeLocallyMedia == 1:
                # Save the photo (as JPEG) with max quality (100%) locally.
                pic.save(photoPathFileName, None, None, 100)

            # Requires backslashes, otherwise pic.save gives exception:
            #   SymbianError: [Errno -28] KErrBadName
            """
            photoPathFileNameWithBackslashes = \
                photoPathFileName.replace("/", "\\")

            # Save the photo (as JPEG) with maximum quality (100%) locally.
            pic.save(photoPathFileNameWithBackslashes, None, None, 100)

        """
        if (accessPointName == u"") and (bluetoothMode != 2):
            DebugPrint("TakePhotoAndUpload(): Not uploading photo.")
            #In order to save the state on the memory card, we call
            #    UploadStateAndFileAndStoreState:
            #UploadStateAndFileAndStoreState(None, cameraId,
            #    crtTime.tm_year, crtTime.tm_mon, crtTime.tm_mday,
            #    crtTime.tm_hour, crtTime.tm_min, crtTime.tm_sec,
            #    None, ICAM_SERVER_NAME, WEBPAGE_UL_GZIPPED_STATE_AND_FILE)

            return
        """
    except IOError:
        DebugPrint("TakePhotoAndUpload_S60(%d): pic.save returned IOError " \
                    "exception when saving %s." % \
                    (cameraId, photoFileName))
        DebugPrintErrorTrace()
    except:
        # traceback.print_exc()
        (exceptionType, exceptionValue, exceptionTraceback) = \
            sys.exc_info()
        errorStr = "TakePhotoAndUpload_S60(%d): while saving " \
                    "photoPathFileName = %s; free_ram = %d, returned " \
                    "exception %s. Bailing out..." % \
                    (cameraId, photoPathFileName, GetFreeRAM(),
                       repr(traceback.format_tb(exceptionTraceback)))

        if MY_DEBUG_UPLOAD_MSG:
            UploadGZippedData(deviceId, errorStr, ICAM_SERVER_NAME,
                              WEBPAGE_UL_GZIPPED_TEXT, None)

        DebugPrint(errorStr)

        if MY_DEBUG_STDERR:
            traceback.print_exc()
            sys.stderr.write("    " + errorStr + "\n")
            sys.stderr.flush()

        return

    if (photoResolutionStr[photoResolutionIndex][1][0] == 0) and \
        (photoResolutionStr[photoResolutionIndex][1][1] == 0):
        """
        A resolution of (0, 0) means we do not upload the photo
            to the server.
        """
        pass
    else:
        """
        We check if we specified that the uploaded photo has the same
            res as the local one
            (photoResolutionStr[photoResolutionIndex][1] = (-1, -1)
            or that the width of the photo saved locally is smaller than
            the width of the photo we want to send over the Internet, in
            which case we send the local photo non-resized.
        """

        if (photoResolutionStr[photoResolutionIndex][1][0] == -1) and \
            (photoResolutionStr[photoResolutionIndex][1][1] == -1) or \
            (localPhotoResolution[cameraId][0] <= \
                photoResolutionStr[photoResolutionIndex][1][0]):
            """
            We check after the width of the photo.
                !!!!we do not check also after the height, i.e.,
                localPhotoResolution[cameraId][1].
            """
            photoResizedPathFileName = photoPathFileName
            picResized = pic #!!!!TODO: probably not necessary --> remove

            DebugPrint("TakePhotoAndUpload_S60: Not resizing photo since the "\
                        "desired resolution of the sent photo is " \
                        "greater or equal to the local resolution.")
        else:
            if pic == None:
                pic = graphics.Image.open(
                                    photoPathFileNameWithBackslashes)

            """
            Since photoResizedPathFileName is "temp.jpg" we do
                not unlink it.
            """
            photoResizedPathFileName = LOCAL_FOLDER_MEDIA_FILES + \
                                                        "/temp.jpg"

            DebugPrint("TakePhotoAndUpload_S60(%d) with photoFileName = %s: " \
                        "pic.resize to (%d, %d)." % \
                        (cameraId, photoFileName,
                           photoResolutionStr[photoResolutionIndex][1][0],
                           photoResolutionStr[photoResolutionIndex][1][1]))

            try:
                """
                From PyS60 2.0 documentation:
                    resize(newsize[, callback=None, keepaspect=0 ]);
                    size is a two-element tuple .
                """
                picResized = \
                    pic.resize(photoResolutionStr[photoResolutionIndex][1])
            except:
                """
                We do this in order to force the garbage collection - but,
                    it doesn't really work it seems...
                """
                pic = None
                picResized = None
                # del pic
                # import gc
                # gc.collect() #does not work in PyS60 1.4.5

                (exceptionType, exceptionValue, exceptionTraceback) = \
                                                        sys.exc_info()
                errorStr = "TakePhotoAndUpload_S60(%d): pic.resize with " \
                            "photoFileName = %s (free_ram = %d) " \
                            "returned exception: " \
                            "exceptionTraceback = %s, " \
                            "exceptionType = %s, exceptionValue = %s. " \
                            "Bailing out..." % \
                            (cameraId, photoFileName, GetFreeRAM(),
                             repr(traceback.format_tb(exceptionTraceback)),
                             str(exceptionType),
                             str(exceptionValue))

                if MY_DEBUG_UPLOAD_MSG:
                    """
                    UploadGZippedData(deviceId,
                        "Exception in TakePhotoAndUpload(%d) with " \
                        "photoFileName = %s: pic.resize - details: " \
                        "free_ram = %d. %s.\n" % \
                        (cameraId, photoFileName, GetFreeRAM(),
                        repr(traceback.format_tb(exceptionTraceback)) ),
                        ICAM_SERVER_NAME, WEBPAGE_UL_GZIPPED_TEXT, None)
                    """
                    UploadGZippedData(deviceId, errorStr, ICAM_SERVER_NAME,
                                        WEBPAGE_UL_GZIPPED_TEXT, None)

                DebugPrint(errorStr)

                if MY_DEBUG_STDERR:
                    sys.stderr.write("    " + errorStr + "\n")

                DebugPrintErrorTrace()

                return

            """
            From PyS60 2.0 documentation:
                transpose(direction[, callback=None ])
            """
            try:
                #          save(filename [,
                #                   callback=None,
                #                   format=None,
                #                   quality=75,
                #                   bpp=24, compression="default"])
                """
                Requires backslashes, otherwise pic.save gives exception:
                    SymbianError: [Errno -28] KErrBadName
                """
                photoResizedPathFileNameWithBackslashes = \
                    photoResizedPathFileName.replace("/", "\\")

                # Save the photo (as JPEG) with photoQuality.
                picResized.save(photoResizedPathFileNameWithBackslashes,
                                None, None, photoQuality)
            except IOError:
                DebugPrint("TakePhotoAndUpload_S60(%d): picResized.save " \
                            "returned exception IOError when saving %s. " \
                            "Bailing out..." % (cameraId, photoFileName))

                return
            except:
                (exceptionType, exceptionValue, exceptionTraceback) = \
                        sys.exc_info()

                # traceback.print_exc()
                errorStr = "TakePhotoAndUpload_S60(%d): picResized.save " \
                            "when saving photoFileName = %s " \
                            "(free_ram = %d) returned exception %s. " \
                            "Bailing out..." % \
                            (cameraId, photoFileName, GetFreeRAM(),
                             repr(traceback.format_tb(exceptionTraceback)))

                if MY_DEBUG_UPLOAD_MSG:
                    UploadGZippedData(deviceId, errorStr,
                            ICAM_SERVER_NAME,
                            WEBPAGE_UL_GZIPPED_TEXT, None)

                DebugPrint(errorStr)

                if MY_DEBUG_STDERR:
                    sys.stderr.write("    " + errorStr + "\n")

                DebugPrintErrorTrace()

                return

        if uploadMediaToIQEngines:
            if (photoResolutionStr[photoResolutionIndex][1][0] == -1) and \
                (photoResolutionStr[photoResolutionIndex][1][1] == -1) and \
                (localPhotoResolution[cameraId][0] <= 640):
                """
                We can upload to IQEngines only photos of maximum
                    (640, 480) resolution.
                """
                IQEnginesPhotoUpload(photoResizedPathFileName)

        try:
            res = UploadStateAndFileAndStoreState(deviceId, cameraId,
                    photoFileName, photoResizedPathFileName,
                    ICAM_SERVER_NAME, WEBPAGE_UL_GZIPPED_STATE_AND_FILE,
                    singleThreaded=False
                )
        except:
            DebugPrintErrorTrace()



def TakePhotoAndUpload_WinCE(cameraId, photoFileName, \
                                        photoPathFileName):
    #photoFileName = None
    #photoPathFileName = None

    try:
        # "/photo.jpg"
        OUTPUT_PHOTO_PATH_FILENAME = LOCAL_FOLDER + "/photo.bmp"

        """
        If photoFileName and photoPathFileName is a .jpg and
            OUTPUT_PHOTO_PATH_FILENAME is a .bmp then we should make the
            first also a .bmp, etc.
        """
        photoFileName = photoFileName[:len(photoFileName) - 4] + \
            OUTPUT_PHOTO_PATH_FILENAME[len(OUTPUT_PHOTO_PATH_FILENAME) - 4:]

        photoPathFileName = \
            photoPathFileName[:len(photoPathFileName) - 4] + \
            OUTPUT_PHOTO_PATH_FILENAME[len(OUTPUT_PHOTO_PATH_FILENAME) - 4:]

        # Requires backslashes?
        OUTPUT_PHOTO_PATH_FILENAMEWithBackslashes = \
            OUTPUT_PHOTO_PATH_FILENAME.replace("/", "\\")

        """
        Requires backslashes, otherwise pic.save gives exception:
            SymbianError: [Errno -28] KErrBadName
        """
        photoPathFileNameWithBackslashes = \
            photoPathFileName.replace("/", "\\")

        if os.path.isfile(OUTPUT_PHOTO_PATH_FILENAME):
            os.unlink(OUTPUT_PHOTO_PATH_FILENAME)

        #WinSpawn(r"\Storage Card\iCam_WinMobile\CameraCapture.exe", [])
        #WinSpawn(r"\Storage Card\iCam\CameraCapture.exe", [])

        #WinSpawn(r"\Storage Card\iCam\take_photo_320_240_bmp_and_exit.exe", [])
        tmpPathFileName = LOCAL_FOLDER + "/take_photo_320_240_bmp_and_exit.exe"

        # I think we require backslashes.
        tmpPathFileNameWithBackslashes = tmpPathFileName.replace("/", "\\")
        WinSpawn(tmpPathFileNameWithBackslashes, [])

        """
        We give this to allow saving completely the media file to the
            disk - but maybe it is not effective.
        """
        time.sleep(5.0)
        # SleepAndPetWatchdog(5.0, False)

        DebugPrint("TakePhotoAndUpload_WinCE(%d): photoFileName = %s, " \
                    "photoPathFileNameWithBackslashes = %s, " \
                    "OUTPUT_PHOTO_PATH_FILENAMEWithBackslashes = %s." \
                    % (cameraId, photoFileName,
                       photoPathFileNameWithBackslashes,
                       OUTPUT_PHOTO_PATH_FILENAMEWithBackslashes))

        """
        Gives often exception:
           "WindowsError: [Error 28] There is not enough space on the disk" .
        """
        #os.rename(OUTPUT_PHOTO_PATH_FILENAMEWithBackslashes,
        #                   photoPathFileNameWithBackslashes)

        """
        Traceback (most recent call last):
          File "\Storage Card\iCam\iCam.py", line 2510, in CopyFile
            dstFile = open(dstFileName, "wb")
        IOError: [Errno 2] The system cannot find the file specified:
            '\\Storage Card\\iCam\\Media\\2011_04_26_13_20_19_000_0.bmp'.
        """
        #CopyFile(OUTPUT_PHOTO_PATH_FILENAMEWithBackslashes,
        #   photoPathFileNameWithBackslashes)

        """
        However, I am able to erase OUTPUT_PHOTO_PATH_FILENAME in the next
            call TakePhoto...() - does this mean I need more than 5
            seconds for the file to be written to disk?
        """

        #res = UploadStateAndFileAndStoreState(deviceId, cameraId,
        #               photoFileName, photoPathFileName, ICAM_SERVER_NAME,
        #               WEBPAGE_UL_GZIPPED_STATE_AND_FILE)
        res = UploadStateAndFileAndStoreState(deviceId, cameraId,
                photoFileName, OUTPUT_PHOTO_PATH_FILENAMEWithBackslashes,
                ICAM_SERVER_NAME, WEBPAGE_UL_GZIPPED_STATE_AND_FILE,
                singleThreaded=True)
    except:
        # return
        DebugPrintErrorTrace()

    return (photoFileName, photoPathFileName)


lastiOSTakePicturePhotoPathFileName = None
def TakePhotoAndUpload_iOS(cameraId, photoFileName, \
                                        photoPathFileName):
    try:
        """
        #From http://www.telesphoreo.org/pipermail/iphone-python/2008-September/000126.html
            "This is actually fairly simple with the PhotoLibrary
                framework. I don't know how to do it offhand, but it
                pretty much has messages for "take a picture to disk". -J"
            "UIImagePickerController doesn't let you take a picture and
                save it to disk, AFAIK it only lets you ask the user to
                choose a picture and optionally take one instead. -J"

        http://forums.macrumors.com/showthread.php?t=462326
            Reference to https://code.google.com/p/iflickr/source/browse/trunk/bkp/PhotoLibrary.h?r=10
            "i dont know if this is what you mean but i was able to get
                the same video ability as on the normal unjailbroken 3gs
                on my 3g by copying the photolibrary.framework file folder
                from a 3gs and paste it on my 3g at
                /var/system/library/privateframeworks.
                im sure you can figure something out by knowing this "

        Camera APIs
            http://forums.macrumors.com/showthread.php?t=462326
                - unofficial camera APIs

            http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIImagePickerController_Class/UIImagePickerController/UIImagePickerController.html
                "Check which media types are available, for the source
                    type youre using, by calling the
                    availableMediaTypesForSourceType: class method.
                    This lets you distinguish between a camera that can be
                    used for video recording and one that can be used only
                    for still images."

            http://developer.apple.com/library/ios/#documentation/AudioVideo/Conceptual/CameraAndPhotoLib_TopicsForIOS/Introduction/Introduction.html#//apple_ref/doc/uid/TP40010400
            http://developer.apple.com/library/ios/#documentation/AudioVideo/Conceptual/AVFoundationPG/Articles/00_Introduction.html#//apple_ref/doc/uid/TP40010188-CH1-SW10
        """

        """
        !!!! Use lock/mutex to synchronize with the takePicture "loop".
        """

        # cameraCapturePicture(photoPathFileName)
        # time.sleep(5.0) #Wait a bit to make sure the photo gets saved.
        # SleepAndPetWatchdog(5.0, False)

        if lastiOSTakePicturePhotoPathFileName is None:
            DebugPrint("TakePhotoAndUpload_iOS(%d): " \
                        "lastiOSTakePicturePhotoPathFileName is None " \
                        "--> bailing out." % cameraId)
            return

        photoPathFileName = lastiOSTakePicturePhotoPathFileName
        photoFileName = os.path.basename(photoPathFileName)
        res = UploadStateAndFileAndStoreState(deviceId, cameraId,
                photoFileName, photoPathFileName,
                ICAM_SERVER_NAME, WEBPAGE_UL_GZIPPED_STATE_AND_FILE,
                singleThreaded=True
            )
    except:
        DebugPrintErrorTrace()

    return (photoFileName, photoPathFileName)


def TakePhotoAndUpload_Android(cameraId, photoFileName, \
                                        photoPathFileName):
    DebugPrint("TakePhotoAndUpload_Android(%d): photoFileName = %s, " \
                "photoPathFileName = %s." % \
                (cameraId, photoFileName, photoPathFileName))

    try:
        #!!!!TODO: I can use here MobileWebCam, as well

        RELY_ON_RUBY_SCRIPT_ICAMTEST = False

        if RELY_ON_RUBY_SCRIPT_ICAMTEST:
            # This checks for photos made with the ruby script iCamTest.rb .
            myResPhoto = GetLatestPhoto(".jpg")

            #!!!!TODO: update photoFileName and photoPathFileName and return them to the caller

            if myResPhoto is not None:
                res = UploadStateAndFileAndStoreState(deviceId, cameraId,
                        myResPhoto[0], myResPhoto[1],
                        ICAM_SERVER_NAME, WEBPAGE_UL_GZIPPED_STATE_AND_FILE,
                        singleThreaded=True
                      )
        else:
            try:
                """
                This starts the ViewFinder (and then stops it and comes back
                    to the old view - the Welcome view).

                Note: while the Viewfinder is ON the UI submenu is no
                    longer available.
                """
                myDroid.cameraCapturePicture(photoPathFileName)

                # Wait a bit to make sure the photo gets saved.
                # time.sleep(5.0)
                SleepAndPetWatchdog(5.0, False)

                res = UploadStateAndFileAndStoreState(deviceId, cameraId,
                            photoFileName, photoPathFileName,
                            ICAM_SERVER_NAME, WEBPAGE_UL_GZIPPED_STATE_AND_FILE,
                            singleThreaded=True
                      )
            except:
                DebugPrintErrorTrace()
    except:
        DebugPrintErrorTrace()



if RASPBIAN_OS:
    import subprocess
def TakePhotoAndUpload_Raspbian(cameraId, photoFileName, \
                                        photoPathFileName):
    DebugPrint("TakePhotoAndUpload_Raspbian(%d): photoFileName = %s, " \
                "photoPathFileName = %s." % \
                (cameraId, photoFileName, photoPathFileName))

    if True:
        # We put the photo in a different folder for each day
        try:
            crtLOCAL_FOLDER_MEDIA_FILES = LOCAL_FOLDER_MEDIA_FILES + "/" + \
                                time.strftime("%Y_%m_%d", GetCurrentDateTime())

            if not os.path.exists(crtLOCAL_FOLDER_MEDIA_FILES):
                os.makedirs(crtLOCAL_FOLDER_MEDIA_FILES)

            photoPathFileName = crtLOCAL_FOLDER_MEDIA_FILES + "/" + \
                                photoFileName
        except:
            DebugPrintErrorTrace()

    try:
        #!!!!TODO: use cameraId
        myId = 0
        #CAM_TL = "fswebcam -d /dev/video%d -r 320x240 --no-timestamp --no-banner" % myId
        CAM_TL = "fswebcam -d /dev/video%d -r 320x240" % myId
        #fileNameTstamp = "TL" + timestampAlex() + ("_%05d.jpg" % index)
        cmd = CAM_TL + " " + photoPathFileName #fileNameTstamp
        try:
            #print("Run:" + cmd)
            subprocess.call([cmd], shell=True)
        except:
            DebugPrintErrorTrace()

        # Wait a bit to make sure the photo gets saved.
        # time.sleep(5.0)
        SleepAndPetWatchdog(5.0, False)

        res = UploadStateAndFileAndStoreState(deviceId, cameraId,
                    photoFileName, photoPathFileName,
                    ICAM_SERVER_NAME, WEBPAGE_UL_GZIPPED_STATE_AND_FILE,
                    singleThreaded=True
              )
    except:
        DebugPrintErrorTrace()



"""
!!!!TODO: The code is stuffy with all the platforms - maybe split it.
    But do not bother to reorganize it much otherwise (for example, it
    is rather OK the fact each OS platform call its own
    UploadStateAndFileAndStoreState()
Take photo at specified resolution, save it and scale it to the desired
    resolution and send it.
"""
def TakePhotoAndUpload(cameraId):
    global photoModeStr, localPhotoResolution
    global deviceId
    global photoModeIndex

    GetGSMLocation()

    # t = datetime.datetime.now()
    """
    See http://pleac.sourceforge.net/pleac_python/datesandtimes.html and
        http://docs.python.org/library/time.html#time.strftime for details.
    """
    # photoFileName = t.strftime("%Y_%m_%d_%H_%M_%S.jpg")
    """
    crtTime = GetCurrentDateTime()
    photoFileName = time.strftime("%Y_%m_%d_%H_%M_%S", crtTime) + \
                        ("_%d.png" % cameraId)
    # RGB24
    photoModeIndex[0] = 2
    """

    photoFileName = GetCurrentDateTimeStringWithMilliseconds() + \
                    "_%s_%d.jpg" % (deviceId, cameraId)
                    #"_%d.jpg" % cameraId

    """
    photoFileName = time.strftime("%Y_%m_%d_%H_%M_%S", crtTime) + \
                                    ("_%d.jpg" % cameraId)
    """
    if storeLocallyMedia == 0:
        if ANDROID_OS:
            photoPathFileName = LOCAL_FOLDER_MEDIA_FILES + "/" + photoFileName
        elif SYMBIAN_OS:
            """
            Since we don't want to store the file, we save it in the RAM drive.
            # photoPathFileName = "D:/iCamTemp.jpg"

            Since we don't want to store the file, we save it in the RAM drive.
            """
            photoPathFileName = LOCAL_FOLDER_TEMP + "/iCamTemp.jpg"
        elif WINDOWS_CE_OS_PYTHONCE:
            photoPathFileName = LOCAL_FOLDER_MEDIA_FILES + "/" + photoFileName
    else:
        photoPathFileName = LOCAL_FOLDER_MEDIA_FILES + "/" + photoFileName

    myText = "TakePhotoAndUpload(%d) with photoPathFileName = %s: " \
                "localPhotoResolution[cameraId] = %s, " \
                "photoMode[cameraId] = %s. " % \
                (cameraId, photoPathFileName,
                    str(localPhotoResolution[cameraId]),
                    photoModeStr[photoModeIndex[cameraId]][1])
    myText += GetTextForState(cameraId)
    DebugPrint(myText)

    if ANDROID_OS:
        TakePhotoAndUpload_Android(cameraId, photoFileName, photoPathFileName)
    elif SYMBIAN_S60_OS:
    # elif SYMBIAN_OS:
        TakePhotoAndUpload_S60(cameraId, photoFileName, photoPathFileName)
    elif iOS_PYOBJC:
        photoFileName, photoPathFileName = TakePhotoAndUpload_iOS(cameraId, \
                                            photoFileName, photoPathFileName)
    elif WINDOWS_CE_OS_PYTHONCE:
        photoFileName, photoPathFileName = TakePhotoAndUpload_WinCE(cameraId, \
                                            photoFileName, photoPathFileName)
    elif RASPBIAN_OS:
        TakePhotoAndUpload_Raspbian(cameraId, photoFileName, photoPathFileName)

    if storeLocallyMedia == 0:
        if ANDROID_OS:
            DebugPrint("Since storeLocallyMedia == 0, now I SHOULD erase it.")
        elif SYMBIAN_OS:
            DebugPrint("Since storeLocallyMedia == 0, I saved the photo on " \
                    "D: and now I erase it.")

        try:
            if SYMBIAN_OS: 
                if _PyS60_1_9_OR_NEWER: #pyS60VersionNumber > 14:
                    mediaUploadedLock.wait()

            os.unlink(photoPathFileName)
        except:
            DebugPrintErrorTrace()


if SYMBIAN_OS:
    videoLock = e32.Ao_lock()


def VideoRecordCallback(errorCode, eventType):
    if SYMBIAN_OS:
        """
        IMPORTANT: From
            http://library.forum.nokia.com/index.jsp?topic=/S60_5th_Edition_Cpp_Developers_Library/GUID-35228542-8C95-4849-A73F-2B4F082F0C44/sdk/doc_source/reference/reference-cpp/Multimedia_Framework/CVideoRecorderUtilityClass.html
        CVideoRecorderUtility::Stop() (invoked by camera.stop_record()):
            "Recording is stopped without sending the MvruoRecordComplete
                message to the client."

        ERecordComplete is not given by camera.stop_record()
         Only time given when video recording was stopped probably by brutal
                exit from app:

           Exiting iCam at 11:47:58 09-03-2011 - command given from the
                cellphone.

           ContinuousVideoRecordAndUpload()::VideoCallback(): at
                2011_03_09_11_48_13_190 we have errorCode = -18,
                statusInfo = 4002.

           Entered PetWatchdog() at 11:48:13 09-03-2011.
        """

        """
        global control_light # what does this represent?!!!!
        if statusInfo == camera.EPrepareComplete:
            control_light = 1
        """

        """
        //cameramodule.cpp
        // A helper function for the implementation of callbacks
        //from C/C++ code to Python callables (modified from appuifwmodule.cpp)
        TInt TPyVidCallback::VideoCameraEvent(TInt aError, TInt aStatus)
          {
              PyGILState_STATE gstate = PyGILState_Ensure();

              TInt error = KErrNone;

              PyObject* arg = Py_BuildValue("(ii)", aError, aStatus);
              ...
          }

        //MvruoEvent defined in
            //C:\Symbian\9.2\S60_3rd_FP1\Epoc32\include\videorecorder.h
            //and in http://library.forum.nokia.com/index.jsp?topic=/S60_5th_Edition_Cpp_Developers_Library/GUID-35228542-8C95-4849-A73F-2B4F082F0C44/sdk/doc_source/reference/reference-cpp/Multimedia_Framework/MVideoRecorderUtilityObserverClass.html
        //TMMFEvent defined in
            //C:\Symbian\9.2\S60_3rd_FP1\Epoc32\include\mmf\common\mmfcontrollerframeworkbase.h:
            //and in http://library.forum.nokia.com/index.jsp?topic=/S60_5th_Edition_Cpp_Developers_Library/GUID-35228542-8C95-4849-A73F-2B4F082F0C44/sdk/doc_source/reference/reference-cpp/Multimedia_Framework/TMMFEventClass.html
        //See also "Symbian OS C++ for Mobile Phones", Volume 2, page 291:
            "Synchronous or asynchronous communication of custom commands
            to the video controller is supported in the same way as for the
            video player utility. Custom callbacks are also supported by the
            observer's MvruoEvent() function. Again, as in the video player,
            this function's usage is not defined by the video player utility
            and thus a video controller supplier may use it to return
            manufacturer-specific information. The event is returned as a
            TMMFEvent, which contains a UID specifying an event type and an
            error code."

        //takephoto.cpp:
        void CVideoCamera::MvruoEvent(const TMMFEvent &aEvent) {
          // XXX modify the callback as there are not enough parameters
          //     this will not show properly to the Python layer
          if(iCallbackSet)
            iCallMe.VideoCameraEvent(aEvent.iErrorCode, aEvent.iEventType.iUid);
        }

        void CVideoCamera::MvruoOpenComplete(TInt aError) {
          iCallMe.VideoCameraEvent(aError, CVideoCamera::EOpenComplete);
          ...
        }

        void CVideoCamera::MvruoPrepareComplete(TInt aError) {
          iCallMe.VideoCameraEvent(aError, CVideoCamera::EPrepareComplete);
          ...
        }

        void CVideoCamera::MvruoRecordComplete(TInt aError) {
          iCallMe.VideoCameraEvent(aError, CVideoCamera::ERecordComplete);
        }

        //cameramodule.h
        enum TObserverEvents
          {
          EOpenComplete = 0xFA0,
          EPrepareComplete,
          ERecordComplete
          };
        and camera.py:
          EOpenComplete=_camera.EOpenComplete
          EPrepareComplete=_camera.EPrepareComplete
          ERecordComplete=_camera.ERecordComplete
        """
        DebugPrint("VideoRecordCallback(): at %s we have errorCode = %d, " \
                    "eventType = %d." % \
                    (GetCurrentDateTimeStringWithMilliseconds(),
                       errorCode, eventType))

        """
        This callback gets invoked first with eventType = camera.EOpenComplete,
            after 145-360ms after start_record().

        Finished prepare for video recording, now ready to record (I guess this
            is equivalent to the red light turning on at least on N95 and N82).
        """
        if eventType == camera.EPrepareComplete:
            global videoRecordStartTime, numFrames
            # GetCurrentDateTimeStringWithMilliseconds()
            videoRecordStartTime = GetTime()
            numFrames = 0
            videoLock.signal()


def SetViewFinderSizeForVideoRecording(cameraId):
    global viewFinderSize, localVideoMode
    global deviceId

    if localVideoMode[cameraId][0][0] >= 320:
        # viewFinderSize = (320, 240)
        if SYMBIAN_S60_2ND_ED:
            """
            We should not get here, since the max video resolution on S60 2nd
                edition is 176x144.
            """
            viewFinderSize = (176, 144)
        elif SYMBIAN_S60_3RD_ED:
            # viewFinderSize = (245, 200)
            viewFinderSize = (220, 180)
        else:
            # This is conserving aspect ratio for (320, 240), as for (176, 144)
            viewFinderSize = (293, 240)
    else:
        """
        On my E7, if we use a viewfinder with size 176x144 then when it starts
         recording some STRANGE green lines appear and the phone might crash...
        """
        # if deviceId == IMEI_E7:
        if SYMBIAN_3:
            #This is conserving aspect ratio for (320, 240), as for (176, 144).
            viewFinderSize = (293, 240)
        else:
            # viewFinderSize = (320, 240)
            viewFinderSize = localVideoMode[cameraId][0]


"""
To avoid the movies to be recorded rotated (e.g., you keep the phone in
    "landscape" mode and you record with the Main camera and when you look on
    the viewer application you see the movie is rotate by 90 degrees)
    YOU HAVE TO specify on your phone to rotate its display along with the
    phone. This can be achieved (at least on S60 3rd edition phones) by going
    at Tools\Settings\General\Personalization\Display\Rotate screen and
    choosing Automatic.

See weekly snippets:
     Nov 6, 2010
       finally realized that the rotation of videos (on S60 3rd edition) is
            caused indeed by the fact iCam is minimized and another app with
            portrait orientation is displayed. I have 2 solutions:
         * I make sure the other app that is maximized is in landscape orientation
            - but what do we do in this case if we take photos/movies with the
            Front camera?
               o another solution is to lock the orientation in landscape for
                    all processes (maybe this is useful
                    http://wiki.forum.nokia.com/index.php/CS001517_-_Lock_application_orientation_in_Qt_for_Symbian ).

         * this is the best solution, but hardest I guess: don't maximize the
                ReV_Watchdog - it happens because of the console watchdog.

         * for photos the situation is even worse: without any orientation
                (nor application) changes N82 takes randomly photos well or
                rotated. N95 as well
                (see Z:\1PhD\ReVival\Logs\NokiaN95\279\Media\):
                although I start in landscape mode, camera reports max res of
                (1600, 1200), so I tell it to take photos only at this
                resolution - first 2 photos are actually taken at 2592x1944 and
                then the following are taken rotated at 1600x1200
                (the right res).

         * also, N95 video records at 176x144 at smaller viewfield than photo

     Nov 16, 2010
       since I saw yesterday (in src\ext\amaretto\camera\src\cameramodule.cpp,
            at cam_take_photo()) that the camera is re-initializatied at every
            camera.take_photo(), it implies that starting the viewfinder is not
            really the thing required to take photos at max resolution and
            angular view. But it is actually the backlight_on parameter set to
            True given to start_finder() that makes all the differences - but
            in fact this translates just to e32.reset_inactivity(). So we only
            need to be in landscape mode (and it is not necessary to have iCam
            focused and) give e32.reset_inactivity() before take_photo() to
            take photos at max resolution and angular view. This is more
            desirable than starting the ViewFinder because: it takes less time,
            uses less the shutter, and also has less chances to give
            KErrAlreadyExists. Also, the issue is that the Power Manager is
            invariably in Portrait mode, so when it kicks in
            (for pauseInterval >= 1 min, usually)
"""
def VideoRecordAndUpload_S60_Before(cameraId):
    # if phoneModel in ["Nokia6120", "NokiaN95", "NokiaN82"]:
    if orientationForThisPhoneModel == "landscape":
        isBackLightOn = True
    else:
        isBackLightOn = False

    """
    We have observed on "landscape"-mode-phones that the screen saver
        (which is INVARIABLY in Portrait mode) can mess up the video
        recordings making them rotated (as in Portrait mode). This is why
        we give e32.reset_inactivity() and also e32.ao_sleep(), to make
        sure we get out of the screen saver mode before the viewfinder
        starts (which is used by the recording).
    """
    try:
        """
        !!!!Maybe optimize a bit for the case if (cameraId == 1): - we can
            PROBABLY take into consideration the fact the screen saver is
            in Portrait mode so we don't need this extra
            e32.reset_inactivity() and e32.ao_sleep(). But what if we are
            not in screen saver mode? :))
        """
        e32.reset_inactivity()

        """
        In case the power saver is started we leave a few seconds for S60
            to get out of the screen saver mode - sometimes it takes
            longer.
        """
        e32.ao_sleep(3)
        """
        if (cameraId == 0) and camera2IsImported:
            # IMPORTANT: AT THE END OF THE RECORD WE MAKE
            #   viewFinderSize = VIEWFINDER_SIZE_ORIG - SEE BELOW,
            #   IN FINALLY.
            viewFinderSize = (320, 240) #(293, 240)
        else:
            # IMPORTANT: AT THE END OF THE RECORD WE MAKE
            #   viewFinderSize = VIEWFINDER_SIZE_ORIG - SEE BELOW,
            #   IN FINALLY.
            viewFinderSize = (176, 144) #(293, 240)
        """

        SetViewFinderSizeForVideoRecording(cameraId)

        # StartViewFinderForCamera(cameraId, isBackLightOn, True)
        StartViewFinderForCamera(cameraId, isBackLightOn, False)

        e32.reset_inactivity()

        # When iCam is not the main app, video recs are rotated.
        # e32.ao_sleep(3)
        # Generated 1 rotated out of 16
        # e32.ao_sleep(2)

        e32.ao_yield()
        # e32.ao_sleep(4)
        # e32.ao_sleep(5)

        """
        We have to make sure (for ex on Nokia 6120) that the viewfinder
            is started if we want to get visual info in the video recorded.
        """
        e32.ao_sleep(2)
        e32.reset_inactivity()
    except:
        DebugPrintErrorTrace()


def VideoRecordAndUpload_S60(cameraId, recordDuration, videoFileName, \
                                                        videoPathFileName):
    global viewFinderSize
    global videoLock

    try:
        """
        Requires backslashes, otherwise camera.start_record() does not
            create any file.
        """
        videoPathFileNameWithBackslashes = \
                            videoPathFileName.replace("/", "\\")

        if (cameraId == 0) and camera2IsImported:
            """
            #camera.start_record(videoPathFileNameWithBackslashes,
            #  VideoRecordCallback,
            #  frameSize = localVideoMode[cameraId][0]) #size = (320, 240))

            #camera.start_record(videoPathFileNameWithBackslashes,
            #   VideoRecordCallback, format="YUV420p",
            #   frameSize=localVideoMode[cameraId][0], frameRate=15.0,
            #   videoType="", audioEnabled=videoAudioEnabled)

            #camera.start_record(videoPathFileNameWithBackslashes,
            #   VideoRecordCallback, format="YUV420p",
            #   frameSize=localVideoMode[cameraId][0],
            #   frameRate=localVideoMode[cameraId][1], videoType="",
            #   audioEnabled = videoAudioEnabled)

            # At least on Nokia E7, to be able to record at 320x240, you
            #   need to specify in camera2.start_record() a
            #   videoType="video/mp4v-es; profile-level-id=3" (or 4)
            #   (videoType="" will record at 176x144).

            #camera.start_record(videoPathFileNameWithBackslashes,
            #   VideoRecordCallback, format="YUV420p",
            #   frameSize=localVideoMode[cameraId][0],
            #   frameRate=localVideoMode[cameraId][1],
            #   videoType="video/mp4v-es; profile-level-id=3",
            #   audioEnabled=videoAudioEnabled)

            # Good:
            # This allows recording on N82 320x240x30fps and 640x480x30fps.
            """
            camera.start_record(
                    videoPathFileNameWithBackslashes, VideoRecordCallback,
                    format="EFormatYUV420Planar",
                    frameSize=localVideoMode[cameraId][0],
                    frameRate=localVideoMode[cameraId][1],
                    videoType="video/mp4v-es; profile-level-id=4",
                    audioEnabled=videoAudioEnabled
                )

            # Here we try Night scene mode.
            #camera.start_record(videoPathFileNameWithBackslashes,
            #   VideoRecordCallback, format="EFormatYUV420Planar",
            #   frameSize=localVideoMode[cameraId][0], frameRate=0.0,
            #   videoType="video/mp4v-es; profile-level-id=4",
            #   audioEnabled=videoAudioEnabled)

            """
            # Bypassing checks for size supported, etc:
            # This one doesn't work because size 1280, 720 is reported as
            #   not being supported - so need to bypass the checks:
            #   camera.start_record(videoPathFileNameWithBackslashes,
            #       VideoRecordCallback, format="EFormatYUV420Planar",
            #       frameSize=(1280, 720), frameRate=25.0,
            #       videoType="video/H264; profile-level-id=42801F",
            #       audioEnabled=videoAudioEnabled) # bitrate = 5000000 .
            #   Maybe try format = "EFormatEncodedH264",

            # I was able to record with this on E7 at 1280x720 at 15 fps -
            #   MAYBE because KMMFVariableVideoBitRate, and AltoRetrato
            #   says that we need to use fixed bitrate (e.g., 5000000)

            #camera._my_video.start_record(camera._handle(),
            #   unicode(videoPathFileNameWithBackslashes),
            #   VideoRecordCallback, (1280, 720),
            #   camera.formatMap["EFormatYUV420Planar"], 25.0,
            #   "video/H264; profile-level-id=42801F", True)

            # I was able to record with this on E7 at 1280x720 at 15 fps -
            #   probably because KMMFVariableVideoBitRate, and AltoRetrato
            #   says that we need to use fixed bitrate (e.g., 5000000).
            camera._my_video.start_record(camera._handle(),
                unicode(videoPathFileNameWithBackslashes),
                VideoRecordCallback, (1280, 720),
                camera.formatMap["EFormatEncodedH264"], 25.0,
                "video/H264; profile-level-id=42801F", True)
            """
        else:
            #camera.start_record(videoPathFileNameWithBackslashes,
            #   VideoRecordCallback, size = (640, 480)) #, fps = 15.0)
            camera.start_record(videoPathFileNameWithBackslashes,
                                            VideoRecordCallback)

        """
        Preparing for video record might take, in some cases, even seconds,
            so we wait for notification that it is ready:
        """
        videoLock.wait()

        e32.ao_sleep(recordDuration)
        """
        If I use after it might give strange results - can record for
           much longer than wanted (e.g., 1:30 min instead of 15 secs) -
           is it maybe because of the petting that writes a file on D: ?

        #SleepAndPetWatchdog(recordDuration)
        #global myTimer
        #myTimer.after(recordDuration, camera.stop_record)
        """

        """
        This operation takes time: it finishes the compression and saves
            the data on card.
        """
        camera.stop_record()

        # camera.stop_finder()

        global videoRecordStartTime
        videoRecordStartTime = -1
        """
        StopViewFinderForCameraCallable comes after
            videoRecordStartTime = -1 because we need this value to update
            properly the ViewFinder section of the menu.
        """
        # StopViewFinderForCameraCallable(False)
        StopViewFinderForCameraCallable(True)
    except:
        (exceptionType, exceptionValue, exceptionTraceback) = \
            sys.exc_info()
        """
        UploadGZippedData(deviceId, "Exception in " \
                "VideoRecordAndUpload(%d) with videoFileName = %s: " \
                "details: free_ram = %d. %s.\n" \
                % (cameraId, videoFileName, GetFreeRAM(),
                    repr(traceback.format_tb(exceptionTraceback)) ),
                    ICAM_SERVER_NAME, WEBPAGE_UL_GZIPPED_TEXT, None)

        # traceback.print_exc()
        """

        errorStr = "VideoRecordAndUpload_S60(%d) with videoFileName %s " \
                    "(free_ram = %d) returned exception %s. " \
                    "Bailing out..." % \
                    (cameraId, videoFileName, GetFreeRAM(),
                       repr(traceback.format_tb(exceptionTraceback)))

        if MY_DEBUG_UPLOAD_MSG:
            UploadGZippedData(deviceId, errorStr, ICAM_SERVER_NAME,
                              WEBPAGE_UL_GZIPPED_TEXT, None)

        DebugPrint(errorStr)

        if MY_DEBUG_STDERR:
            sys.stderr.write("    " + errorStr + "\n")
        DebugPrintErrorTrace()
        """
        viewFinderSize = VIEWFINDER_SIZE_ORIG
        # Even if we give return, finally is sill executed before returning
        #    from this function.
        return
        """
    """
    # !!!!This finally doesn't work on PyS60 1.4.5 (Python 2.2) - WHY?!!!!
    finally:
        viewFinderSize = VIEWFINDER_SIZE_ORIG
    """
    viewFinderSize = VIEWFINDER_SIZE_ORIG


def VideoRecordAndUpload_WinCE(cameraId, recordDuration, \
                                        videoFileName, videoPathFileName):

    # if accessPointName == u"":
    #    print "VideoRecordAndUpload(): Not uploading movie."
    #    return
    """
    if os.path.isfile(videoPathFileName):
        os.unlink(videoPathFileName)

    # It takes between 1:30 - 5 minutes on HTC Touch Cruise to encode the
    #   video to .asf ... :)
    WinSpawn(r"\Storage Card\iCam\video_rec_mute_30sec_ASF_and_exit.exe",
                [])
    """

    # Calling the Camera.exe application directly :)

    # Doesn't work
    # WinSpawn(r"\Storage Card\iCam\TimeLapsePhotos.mscr", [])

    # The MortScript (.mscr) doesn't work well when called from iCam.py -
    #   the Sleep() in .mscr works incorrectly (usually sleeps very little
    #   - ~nothing for 30 secs, etc).
    #WinSpawn(r"\Storage Card\iCam\CamAuto.exe", [])

    # Camera.exe application runs in a separate process.
    # Finding the most recent video.
    try:
        actualFileName = None

        pathFolderName = WINCE_MEDIA_FOLDER
        folderContent = os.listdir(pathFolderName)
        """
        Use reverse = False to send first the oldest ones (like this you
            send in chronological order). Use reverse = True for sending
            first the most recent ones.
        """
        # sortedFolderContent = sorted(folderContent, reverse = False)

        """
        sort() without parameters is the ONLY one that works in Python 2.2.
            (Info on sort at http://wiki.python.org/moin/HowTo/Sorting/.)
        """
        folderContent.sort()
        sortedFolderContent = folderContent

        DebugPrint("VideoRecordAndUpload_WinCE(): " \
                    "sortedFolderContent = %s." % sortedFolderContent)

        """
        !!!!I should check for len(sortedFolderContent) > 2
        """
        actualFileName = sortedFolderContent[len(sortedFolderContent) - 1]
        videoPathFileName = pathFolderName + "/" + actualFileName
        try:
            fInput = open(videoPathFileName, "rb")
            # mediaFileData = fInput.read()
            fInput.close()
        except:
            DebugPrintErrorTrace()
            """
            If I can't open the file I presume it's because the Camera app
                is still recording on this file. So I use the previous
                video recorded.
            """
            actualFileName = \
                sortedFolderContent[len(sortedFolderContent) - 2]
            videoPathFileName = pathFolderName + "/" + actualFileName

        if actualFileName.find("VIDEO") == -1:
            return
        """
        else:
            # !!!!We assume this is the most recent movie - I should check
            #   after creation time.
            videoPathFileName = "/Storage Card/DCIM/100MEDIA/" + \
                                actualFileName
        for actualFileName in sortedFolderContent:
            if (actualFileName.find(STDERR_FILENAME_PREFIX) != -1) or
                (actualFileName.find(STDOUT_FILENAME_PREFIX) != -1):
        """
        try:
            if MY_DEBUG_STDOUT:
                # See http://docs.python.org/library/os.path.html
                # print "File was last modified (12 hour format):"
                print "VideoRecordAndUpload_WinCE(): File %s has the write " \
                        "(last modification?) time (getmtime) %s." % \
                        (videoPathFileName,
                            time.strftime("%m/%d/%Y %I:%M:%S %p",
                            time.localtime(
                            os.path.getmtime(videoPathFileName))))

                """
                NOT RELEVANT on WinCE - returns correct day, but hour is
                    12:00:00 AM:
                    print "VideoRecordAndUpload(): File %s has " \
                            "the last access time (getatime) %s." % \
                            (videoPathFileName, time.strftime(
                                "%m/%d/%Y %I:%M:%S %p",
                                time.localtime(os.path.getatime(
                                    videoPathFileName))))
                """
                print "VideoRecordAndUpload_WinCE(): File %s has the " \
                        "creation time (on Windows) (getctime) %s." % \
                        (videoPathFileName,
                           time.strftime("%m/%d/%Y %I:%M:%S %p",
                           time.localtime(os.path.getctime(
                                videoPathFileName))))
                sys.stdout.flush()
        except:
            DebugPrintErrorTrace()

        try:
            """
            Use the modification time of the video and preserve its
                extion (.mp4, .3gp, etc).
            """
            videoEndTime = \
                time.localtime(os.path.getmtime(videoPathFileName))

            """
            See http://pleac.sourceforge.net/pleac_python/datesandtimes.html
                and http://docs.python.org/library/time.html#time.strftime
                for details.
            """
            videoFileName = time.strftime("%Y_%m_%d_%H_%M_%S_000", \
                                                    videoEndTime) + \
                                "_%s_%d" % (deviceId, cameraId) + \
                                actualFileName[len(actualFileName) - 4:]
        except:
            DebugPrintErrorTrace()
    except:
        """
        Preserve the extention of the media file (by default it is a .3gp).
        # videoFileName = videoFileName[ : len(videoFileName) - 4] +
        #   actualFileName[len(actualFileName) - 4 : ]
        """
        DebugPrintErrorTrace()

    try:
        # Not sure if it requires backslashes
        videoPathFileName = videoPathFileName.replace("/", "\\")

        """
        We give this to allow saving completely the media file to the disk -
            but maybe it is not effective.
        """
        time.sleep(5.0)
    except:
        DebugPrintErrorTrace()

    return (videoFileName, videoPathFileName, actualFileName)


def VideoRecordAndUpload_Android(cameraId, recordDuration, \
                                        videoFileName, videoPathFileName):
    try:
        """
        See https://code.google.com/p/android-scripting/wiki/ApiReference#recorderCaptureVideo:
        recorderCaptureVideo(String targetPath, Double duration[optional],
            Boolean recordAudio[optional, default true])
        """

        # myDroid.recorderCaptureVideo(videoPathFileName, recordDuration, True)
        #if True:
        if False:
            myDroid.recorderCaptureVideo(videoPathFileName, recordDuration,
                                                        videoAudioEnabled)
        else:
            """
            #!!!!TODO: try out different resolutions with recorderStartVideo()
            From M:\1Hg5\android-scripting\android\Common\src\com\googlecode\android_scripting\facade\MediaRecorderFacade.java
            @Rpc(description = "Records video from the camera and saves it to the given location. "
                + "\nDuration specifies the maximum duration of the recording session. "
                + "\nIf duration is 0 this method will return and the recording will only be stopped "
                + "\nwhen recorderStop is called or when a scripts exits. "
                + "\nOtherwise it will block for the time period equal to the duration argument."
                + "\nvideoSize: 0=160x120, 1=320x240, 2=352x288, 3=640x480, 4=800x480.")
            public void recorderStartVideo(@RpcParameter(name = "targetPath") String targetPath,
                @RpcParameter(name = "duration") @RpcDefault("0") Integer duration,
                @RpcParameter(name = "videoSize") @RpcDefault("1") Integer videoSize) throws Exception {
            """
            myDroid.recorderStartVideo(videoPathFileName, recordDuration, 3) # 640x480

        myDroid.recorderStop()

        # Wait for the video to be saved before trying to send the file.
        time.sleep(5.0)
    except:
        DebugPrintErrorTrace()


def VideoRecordAndUpload(cameraId, recordDuration):
    global accessPointName  #, bluetoothMode
    global LOCAL_FOLDER_MEDIA_FILES, deviceId

    DebugPrint("Entered VideoRecordAndUpload() at %s." % \
                            GetCurrentDateTimeStringNice())

    # if SYMBIAN_OS:
    if SYMBIAN_S60_OS:
        VideoRecordAndUpload_S60_Before(cameraId)

    GetGSMLocation()

    # crtTime = GetCurrentDateTime()
    """
    See http://pleac.sourceforge.net/pleac_python/datesandtimes.html and
        http://docs.python.org/library/time.html#time.strftime for details.
    """
    #videoFileName = time.strftime("%Y_%m_%d_%H_%M_%S", crtTime) + \
    #   ("_%d.3gp" % cameraId)
    videoFileName = GetCurrentDateTimeStringWithMilliseconds() + \
                                "_%s_%d.3gp" % (deviceId, cameraId)

    if storeLocallyMedia == 0:
        if ANDROID_OS:
            """
            Since we don't want to save the file on mem card, we save in
                the RAM drive.
            """
            videoPathFileName = "/sdcard/iCamTemp.3gp"
        elif SYMBIAN_OS:
            """
            Since we don't want to save the file on mem card, we save in
                the RAM drive.
            """
            # videoPathFileName = "D:/iCamTemp.3gp"
            videoPathFileName = LOCAL_FOLDER_TEMP + "/iCamTemp.3gp"
        elif WINDOWS_CE_OS_PYTHONCE:
            """
            We actually do not use this value, since we should have
                storeLocallyMedia == 1 (or 2).
            """
            videoPathFileName = "[N/A]"
    else:
        videoPathFileName = LOCAL_FOLDER_MEDIA_FILES + "/" + videoFileName

    DebugPrint("VideoRecordAndUpload(%d) with videoFileName = %s, " \
                    "videoPathFileName = %s, recordDuration = %d, " \
                    "localVideoMode[cameraId] = %s. " % \
                    (cameraId, videoFileName, videoPathFileName,
                       recordDuration, str(localVideoMode[cameraId])) + \
                GetTextForState(cameraId))

    if ANDROID_OS:
        VideoRecordAndUpload_Android(cameraId, recordDuration, \
                                        videoFileName, videoPathFileName)
    elif SYMBIAN_S60_OS:
    # elif SYMBIAN_OS:
        VideoRecordAndUpload_S60(cameraId, recordDuration, \
                                        videoFileName, videoPathFileName)
    elif WINDOWS_CE_OS_PYTHONCE:
        (videoFileName, videoPathFileName, actualFileName) = \
                                    VideoRecordAndUpload_WinCE( \
                                        cameraId, recordDuration, \
                                        videoFileName, videoPathFileName)

    """
    res = UploadStateAndFileAndStoreState(deviceId, cameraId,
            videoFileName, OUTPUT_VIDEO_PATH_FILENAME,
            ICAM_SERVER_NAME, WEBPAGE_UL_GZIPPED_STATE_AND_FILE)
    """
    res = UploadStateAndFileAndStoreState(deviceId, cameraId,
                        videoFileName, videoPathFileName,
                        ICAM_SERVER_NAME, WEBPAGE_UL_GZIPPED_STATE_AND_FILE,
                        singleThreaded=False)

    #!!!!if res == -1: return

    if WINDOWS_CE_OS_PYTHONCE:
        try:
            if (storeLocallyMedia == 1) and (actualFileName is not None):
                DebugPrint("VideoRecordAndUpload(): Moving video in %s." % \
                                                    WINCE_SENT_MEDIA_FOLDER)

                if not os.path.exists(WINCE_SENT_MEDIA_FOLDER):
                    os.makedirs(WINCE_SENT_MEDIA_FOLDER)
                MoveFileBetweenAnyDrives(videoPathFileName, \
                            WINCE_SENT_MEDIA_FOLDER + "/" + actualFileName)
        except:
            DebugPrintErrorTrace()

    if storeLocallyMedia == 0:
        DebugPrint("VideoRecordAndUpload(): Since storeLocallyMedia == 0, " \
                    "now I erase it (note that on S60 I saved the movie " \
                    "on D:).")

        try:
            if SYMBIAN_OS:
                if _PyS60_1_9_OR_NEWER: #pyS60VersionNumber > 14:
                    mediaUploadedLock.wait()
            """
            If WINDOWS_CE_OS_PYTHONCE and (res == -1) we keep the video
                (normally in DCIM folder)
            """
            if (WINDOWS_CE_OS_PYTHONCE == False) or \
                        ((WINDOWS_CE_OS_PYTHONCE == True) and (res != -1)):
                os.unlink(videoPathFileName)
            else:
                pass
        except OSError:
            """
            Quitting application since it might have problems with video
                recorder remaining hanged.
            """
            DebugPrint("VideoRecordAndUpload(): received exception of type " \
                        "exceptions.OSError.")

            DebugPrintErrorTrace()
            """
            At least my N95 with bad display gives sometimes this exception:
                "OSError: [Errno 13] Permission denied: 'D:/iCamTemp.3gp'"
            This exception started appearing immediately after an exception
                YouTubeError:
                {'status': 403, 'body': "<?xml version='1.0'
                encoding='UTF-8'?><errors><error><domain>yt:quota</domain><code>too_many_recent_calls</code></error></errors>", 'reason': 'Forbidden'}
                I guess this means the youtubeClient.InsertVideoEntry() doesn't
                    release this file, hence the exception when trying to
                    os.unlink() this file. Also it seems video recording
                    doesn't work either anymore (again probably because the
                    file D:/iCamTemp.3gp is still used by some thread? of
                    youtubeClient.InsertVideoEntry().

            LESS PROBABLE: I think it has to do with the fact the video
                recording seems to have some issues (I have noticed this only
                on this phone) - I think what happens is that the video
                recorder remains hanged and doesn't release the file
                'D:/iCamTemp.3gp', hence the exception when trying to
                os.unlink() this file.
            """
        except:

            """
            A possible solution is to use a different name than
                D:/iCamTemp.3gp.:
            We don't give Quit(), because this exception can be triggered by
                the fact we want to upload the video to YouTube, but receive a
                timeout exception and save the movie to the Unsent folder,
                hence we don't have the file anymore as videoPathFileName.
            """
            DebugPrintErrorTrace()


MULTITHREADED_VIDEORECORD_AND_UPLOAD = True
"""
For Symbian OS:
    VF, TakePhoto, VideoRec, AudioRec.
        - NOTE: .save() and .resize() can be async

    Burst (Turbo) VideoRecord mode:
        - accomplished by:
            keeping on forever the VF
            uses multithreading to do video recording of new movie and sending
                the previous movie simultaneously.
        The standard VideoRecordAndUpload, which is useful if we use both
                cameras, does the following:

            # In case the power saver is started we leave a few seconds for
            #    S60 to come back to landscape orientation - sometimes it
            #    takes longer.
            e32.ao_sleep(3)
            StartViewFinderForCamera
                camera.stop_finder()
                camera.release()
                #camera._my_camera = camera._camera.Camera(cameraId)
                camera.UseCamera(cameraId)

            # We have to make sure (for ex on Nokia 6120) that the viewfinder
            #   is started if we want to get visual info in the video recorded.
            e32.ao_sleep(2)

            # This operation takes time: it finishes the compression and saves
            #   the data on card.
            stop_record()
            1 sec pause

          As we can see we pause for 6 seconds explicitely (which are MANDATORY
            TO USE TO PERFORM VIDEO RECORDING in the right orientation, etc),
            and most likely 1-4 more in StartViewFinderForCamera and
            stop_record().

        So, in case we don't use both cameras, we should use
            ContinuousVideoRecordAndUpload() since it is more efficient - fewer
            operations which probably result in less mechanical stress on the
            shutter and the rest opical system.
"""
def ContinuousVideoRecordAndUpload(cameraId, recordDuration):
    global accessPointName  #, bluetoothMode
    global deviceId, viewFinderSize, videoLock
    global LOCAL_FOLDER_MEDIA_FILES, LOCAL_FOLDER_UNSENT_FILES
    global uploadUnsentData

    uploadUnsentData = 0

    """
    We save the unsent packet in the ram drive.
    In fact we should not even save the Unsent packets -simply drop them maybe.
    """

    # LOCAL_FOLDER_UNSENT_FILES = "D:/Unsent"
    LOCAL_FOLDER_UNSENT_FILES = LOCAL_FOLDER_TEMP + "/Unsent"

    if not os.path.exists(LOCAL_FOLDER_UNSENT_FILES):
        os.makedirs(LOCAL_FOLDER_UNSENT_FILES)

    DebugPrint("Entered ContinuousVideoRecordAndUpload() at %s." % \
                                GetCurrentDateTimeStringNice())

    if SYMBIAN_OS:
        if orientationForThisPhoneModel == "landscape":
        #if phoneModel in ["Nokia6120", "NokiaN95", "NokiaN82"]:
            isBackLightOn = True
        else:
            isBackLightOn = False

        """
        We have observed on "landscape"-mode-phones that the screen saver
            (which is INVARIABLY in Portrait mode) can mess up the video
            recordings making them rotated (as in Portrait mode). This is why
            we give e32.reset_inactivity() and also e32.ao_sleep(2), to make
            sure we are in landscape mode before the viewfinder starts (which
            is used by the recording).
        """
        try:
            e32.reset_inactivity()
            """
            In case the power saver is started we leave a few seconds for S60
                to come back to landscape orientation - sometimes it takes
                longer.
            """
            e32.ao_sleep(3)
            """
            if (cameraId == 0) and camera2IsImported:
                # IMPORTANT: AT THE END OF THE RECORD WE MAKE
                #   viewFinderSize = VIEWFINDER_SIZE_ORIG - SEE BELOW, I
                #    FINALLY.
                viewFinderSize = (320, 240) #(293, 240)
            else:
                # IMPORTANT: AT THE END OF THE RECORD WE MAKE
                #   viewFinderSize = VIEWFINDER_SIZE_ORIG - SEE BELOW, IN
                #   FINALLY.
                viewFinderSize = (176, 144) #(293, 240)
            """

            SetViewFinderSizeForVideoRecording(cameraId)
            # StartViewFinderForCamera(cameraId, isBackLightOn, True)
            StartViewFinderForCamera(cameraId, isBackLightOn, False)
            e32.reset_inactivity()

            # When iCam is not the main app, video recs are rotated.
            #e32.ao_sleep(3)
            # Generated 1 rotated out of 16.
            # e32.ao_sleep(2)

            e32.ao_yield()
            # e32.ao_sleep(4)
            # e32.ao_sleep(5)
            """
            We have to make sure (for ex on Nokia 6120) that the viewfinder is
                started if we want to get visual info in the video recorded.
            """
            e32.ao_sleep(2)
            e32.reset_inactivity()
        except:
            DebugPrintErrorTrace()

        GetGSMLocation()

        # Nokia N82
        if deviceId == IMEI_N82:
            """
            LOCAL_FOLDER_AUX = "D:/iCam"
            if os.path.exists(LOCAL_FOLDER_AUX) == False:
                os.makedirs(LOCAL_FOLDER_AUX)
            """
            LOCAL_FOLDER_MEDIA_FILES = LOCAL_FOLDER_TEMP + "/Media"
            if not os.path.exists(LOCAL_FOLDER_MEDIA_FILES):
                os.makedirs(LOCAL_FOLDER_MEDIA_FILES)

        counterVideoRecords = 0

        while True:
            NUM_VIDEO_RECORDS_BETWEEN_DOWNLOAD_COMMANDS = 2

            if counterVideoRecords == 0:
                hasDownloadedNewCmd = DownloadCommands()

            if counterVideoRecords \
                        == NUM_VIDEO_RECORDS_BETWEEN_DOWNLOAD_COMMANDS - 1:
                counterVideoRecords = 0
            else:
                counterVideoRecords += 1

            #crtTime = GetCurrentDateTime()

            """
            See http://pleac.sourceforge.net/pleac_python/datesandtimes.html
                and http://docs.python.org/library/time.html#time.strftime
                for details.
            """
            #videoFileName = time.strftime("%Y_%m_%d_%H_%M_%S", crtTime) + \
            #   ("_%d.3gp" % cameraId)
            videoFileName = GetCurrentDateTimeStringWithMilliseconds() + \
                                "_%s_%d.3gp" % (deviceId, cameraId)
                                #+ "_%d.3gp" % cameraId

            if storeLocallyMedia == 0:
                # Since we don't want to save the file on mem card, we save in
                #   the RAM drive.
                #videoPathFileName = "D:/iCamTemp.3gp"

                # Since we don't want to save the file on mem card, we save in
                #   the RAM drive.
                videoPathFileName = LOCAL_FOLDER_TEMP + "/iCamTemp.3gp"
            else:
                videoPathFileName = LOCAL_FOLDER_MEDIA_FILES + "/" + \
                                        videoFileName

            DebugPrint("ContinuousVideoRecordAndUpload(%d) with " \
                    "videoFileName = %s, videoPathFileName = %s, " \
                    "recordDuration = %d, localVideoMode[cameraId] = %s. " % \
                    (cameraId, videoFileName, videoPathFileName,
                       recordDuration, str(localVideoMode[cameraId])) + \
                    GetTextForState(cameraId))
            #sys.stdout.flush()

            try:
                """
                Requires backslashes, otherwise camera.start_record() does
                    not create any file.
                """
                videoPathFileNameWithBackslashes = \
                    videoPathFileName.replace("/", "\\")

                if (cameraId == 0) and camera2IsImported:
                    """
                    #camera.start_record(videoPathFileNameWithBackslashes,
                    #    VideoRecordCallback,
                    #    size = localVideoMode[cameraId][0])
                    #    #size = (320, 240))

                    #camera.start_record(videoPathFileNameWithBackslashes,
                    #    VideoRecordCallback, format = "YUV420p",
                    #    frameSize = localVideoMode[cameraId][0],
                    #    frameRate = 15.0, videoType = "",
                    #   audioEnabled = videoAudioEnabled)

                    #camera.start_record(videoPathFileNameWithBackslashes,
                    #    VideoRecordCallback, size = (640, 480)) #, fps = 15.0)

                    #camera.start_record(videoPathFileNameWithBackslashes,
                    #   VideoRecordCallback, format = "YUV420p",
                    #   frameSize = localVideoMode[cameraId][0],
                    #   frameRate = localVideoMode[cameraId][1],
                    #   videoType = "", audioEnabled = videoAudioEnabled)
                    """

                    camera.start_record(
                            videoPathFileNameWithBackslashes,
                            VideoRecordCallback,
                            format="EFormatYUV420Planar",
                            frameSize=localVideoMode[cameraId][0],
                            frameRate=localVideoMode[cameraId][1],
                            videoType="video/mp4v-es; profile-level-id=4",
                            audioEnabled=videoAudioEnabled
                        )
                else:
                    camera.start_record(videoPathFileNameWithBackslashes,
                            VideoRecordCallback)

                #myResult=camera.start_record(videoPathFileNameWithBackslashes,
                #   VideoRecordCallback, size=(320, 240)) #it returns !!!!

                """
                Preparing for video record might take, in some cases, even
                    seconds, so we wait for notification, it is ready:
                """
                videoLock.wait()

                e32.ao_sleep(recordDuration)
                """
                If I use after it might give strange results - can record for
                    much longer than wanted (e.g., 1:30 min instead of 15
                    secs) - is it maybe because of the petting that writes a
                    file on D: ?
                """
                # SleepAndPetWatchdog(recordDuration)
                # global myTimer
                # myTimer.after(recordDuration, camera.stop_record)

                DebugPrint("ContinuousVideoRecordAndUpload(): before " \
                        "camera.stop_record(), " \
                        "GetCurrentDateTimeStringWithMilliseconds() = %s." % \
                        GetCurrentDateTimeStringWithMilliseconds())

                """
                This operation takes time: it finishes the compression and
                    saves the data on card.
                """
                camera.stop_record()

                DebugPrint("ContinuousVideoRecordAndUpload(): after " \
                        "camera.stop_record(), " \
                        "GetCurrentDateTimeStringWithMilliseconds() = %s." % \
                        GetCurrentDateTimeStringWithMilliseconds())
                """
                camera.stop_record()
                camera.stop_finder()
                """
            except:
                (exceptionType, exceptionValue, exceptionTraceback) = \
                    sys.exc_info()
                """
                UploadGZippedData(deviceId, "Exception in " \
                    "VideoRecordAndUpload(%d) with videoFileName = %s: " \
                    "details: free_ram = %d. %s.\n" \
                    % (cameraId, videoFileName, GetFreeRAM(),
                        repr(traceback.format_tb(exceptionTraceback)) ),
                        ICAM_SERVER_NAME, WEBPAGE_UL_GZIPPED_TEXT, None)
                """
                # traceback.print_exc()
                errorStr = "ContinuousVideoRecordAndUpload(%d) with " \
                            "videoFileName %s (free_ram = %d) returned " \
                            "exception %s. Bailing out..." % \
                            (cameraId, videoFileName, GetFreeRAM(),
                               repr(traceback.format_tb(exceptionTraceback)))

                if MY_DEBUG_UPLOAD_MSG:
                    UploadGZippedData(deviceId, errorStr,
                            ICAM_SERVER_NAME, WEBPAGE_UL_GZIPPED_TEXT, None)

                DebugPrint(errorStr)

                if MY_DEBUG_STDERR:
                    sys.stderr.write("    " + errorStr + "\n")

                DebugPrintErrorTrace()

                viewFinderSize = VIEWFINDER_SIZE_ORIG

                """
                Even if we give return, finally is sill executed before
                    returning from this function.
                """
                return
            """
            # !!!!This finally doesn't work on PyS60 1.4.5 (Python 2.2) - WHY?
            finally:
                viewFinderSize = VIEWFINDER_SIZE_ORIG
            """
            viewFinderSize = VIEWFINDER_SIZE_ORIG

            # if accessPointName == u"":
            #    print "VideoRecordAndUpload(): Not uploading movie."
            #    return

            def UploadThread(videoFileName, videoPathFileName):
                """
                http://stackoverflow.com/questions/2576534/does-pythons-httplib-httpconnection-block
                "Although you can do asynchronous requests, you will have to
                    make you entire program async-friendly. Async does not
                    magically make your code non-blocking. It would be much
                    easier to do the request in another thread or process if
                    you don't want to block your main loop."
                """

                """
                We sleep to give time to the phone to complete saving the
                    video file.
                It would be nice to use instead of sleep() a lock and signal
                    in VideoRecordCallback when it receives
                    camera.ERecordComplete, but ERecordComplete is not issued
                    by camera.stop_record().
                """
                e32.ao_sleep(2)

                """
                try:
                    camera.stop_finder()
                    DebugPrint("VideoRecordAndUpload(): after " \
                            "camera.stop_finder(), " \
                            "GetCurrentDateTimeStringWithMilliseconds() = %s." % \
                            GetCurrentDateTimeStringWithMilliseconds())
                except:
                    DebugPrintErrorTrace()
                    sys.stdout.flush()
                """
                UploadStateAndFileAndStoreState(deviceId, cameraId,
                        videoFileName, videoPathFileName,
                        ICAM_SERVER_NAME, WEBPAGE_UL_GZIPPED_STATE_AND_FILE,
                        singleThreaded=True)

            global MULTITHREADED_VIDEORECORD_AND_UPLOAD

            if MULTITHREADED_VIDEORECORD_AND_UPLOAD:
                """
                Multithreaded version --> it tries to film as much as possible,
                    with very little pause between films, since we use a
                    separate thread for video upload.
                """
                """
                thread.start_new_thread(UploadThread, (videoFileName,
                        videoPathFileName))
                """
                MyThreadStart(UploadThread, (videoFileName, videoPathFileName))
            else:
                # Single-threaded version.
                res = UploadStateAndFileAndStoreState(deviceId, cameraId,
                        videoFileName, videoPathFileName,
                        ICAM_SERVER_NAME, WEBPAGE_UL_GZIPPED_STATE_AND_FILE,
                        singleThreaded=True
                    )

            if not MULTITHREADED_VIDEORECORD_AND_UPLOAD:
                if storeLocallyMedia == 0:
                    DebugPrint("ContinuousVideoRecordAndUpload(): Since " \
                            "storeLocallyMedia == 0, I saved the movie on " \
                            "D: and now I erase it.")
                    try:
                        os.unlink(videoPathFileName)
                    except:
                        DebugPrintErrorTrace()
        """
        When getting out of the Burst mode - which I should do, instead of
            having a FOREVER loop.
        """
        global videoRecordStartTime
        videoRecordStartTime = -1


def AudioRecordAndUpload(recordDuration):
    global LOCAL_FOLDER_MEDIA_FILES, accessPointName, deviceId

    """
    !!!!TODO: use pyjnius example ex_audio_rec.py

    from jnius import autoclass
    from time import sleep

    # get the needed Java class
    MediaRecorder = autoclass('android.media.MediaRecorder')
    AudioSource = autoclass('android.media.MediaRecorder$AudioSource')
    OutputFormat = autoclass('android.media.MediaRecorder$OutputFormat')
    AudioEncoder = autoclass('android.media.MediaRecorder$AudioEncoder')

    # create out recorder
    mRecorder = MediaRecorder()
    mRecorder.setAudioSource(AudioSource.MIC)
    mRecorder.setOutputFormat(OutputFormat.THREE_GPP)
    mRecorder.setOutputFile('/sdcard/testrecorder.3gp')
    #Alex: this was wrong
    #mRecorder.setAudioEncoder(AudioEncoder.ARM_NB)
    mRecorder.setAudioEncoder(AudioEncoder.ARM_NB)
    mRecorder.prepare()

    # record 5 seconds
    mRecorder.start()
    sleep(5)
    mRecorder.stop()
    mRecorder.release()


    if ANDROID_OS:
        recorderStartMicrophone
        recorderStop
    """

    # crtTime = GetCurrentDateTime()
    """
    See http://pleac.sourceforge.net/pleac_python/datesandtimes.html and
        http://docs.python.org/library/time.html#time.strftime for details.
    """
    # audioFileName = time.strftime("%Y_%m_%d_%H_%M_%S.amr", crtTime)
    audioFileName = GetCurrentDateTimeStringWithMilliseconds() + \
                            "_%s_%d.amr" % (deviceId, 2) # We make cameraId = 2

    if storeLocallyMedia == 0:
        # Since we don't want to store the file, we save in the RAM drive.
        # audioPathFileName = "D:/iCamTemp.amr"

        # Since we don't want to store the file, we save in the RAM drive.
        audioPathFileName = LOCAL_FOLDER_TEMP + "/iCamTemp.amr"
    else:
        audioPathFileName = LOCAL_FOLDER_MEDIA_FILES + "/" + \
                                        audioFileName

    DebugPrint("AudioRecordAndUpload() with audioFileName = %s and " \
            "recordDuration = %d. " % (audioFileName, recordDuration) + \
            GetTextForState(0))

    if SYMBIAN_OS:
        try:
            """
            Requires backslashes, otherwise audio.Sound.open() PROBABLY does
                not create any file.
            """
            audioPathFileNameWithBackslashes = \
                audioPathFileName.replace("/", "\\")
            mySound = audio.Sound.open(audioPathFileNameWithBackslashes)
            mySound.record()
            SleepAndPetWatchdog(recordDuration, False)
            # e32.ao_sleep(recordDuration)
            mySound.stop()
        except:
            (exceptionType, exceptionValue, exceptionTraceback)= sys.exc_info()
            errorStr = "AudioRecordAndUpload() with audioFileName %s " \
                        "(free_ram = %d) returned exception %s. " \
                        "Bailing out..." % \
                        (audioFileName, GetFreeRAM(),
                           repr(traceback.format_tb(exceptionTraceback)))

            if MY_DEBUG_UPLOAD_MSG:
                UploadGZippedData(deviceId, errorStr, ICAM_SERVER_NAME,
                                  WEBPAGE_UL_GZIPPED_TEXT, None)

            DebugPrint(errorStr)

            if MY_DEBUG_STDERR:
                sys.stderr.write("    " + errorStr + "\n")
            DebugPrintErrorTrace()

            return

    # if accessPointName == u"":
    #    print "AudioRecordAndUpload(): Not uploading movie."
    #    sys.stdout.flush()
    #    return

    res = UploadStateAndFileAndStoreState(
                        deviceId, 2, # We make cameraId = 2
                        audioFileName, audioPathFileName,
                        ICAM_SERVER_NAME, WEBPAGE_UL_GZIPPED_STATE_AND_FILE,
                        singleThreaded=True
                        )

    if storeLocallyMedia == 0:
        DebugPrint("Since storeLocallyMedia == 0, I saved the audio file " \
                    "on D: and now I erase it.")

        try:
            """
            if SYMBIAN_OS:
                if _PyS60_1_9_OR_NEWER: #pyS60VersionNumber > 14:
                    mediaUploadedLock.wait()
            """
            os.unlink(audioPathFileName)
        except:
            DebugPrintErrorTrace()


def GetBatteryLevelPercentage():
    try:
        if ANDROID_OS:
            """
            print "myDroid.batteryCheckPresent() =",
                myDroid.batteryCheckPresent()

            print "myDroid.batteryGetLevel() =", myDroid.batteryGetLevel()

            print "myDroid.batteryGetHealth() =", myDroid.batteryGetHealth()

            print "myDroid.batteryGetPlugType() =",
                myDroid.batteryGetPlugType()

            print "myDroid.batteryGetTechnology() =",
                myDroid.batteryGetTechnology()

            print "myDroid.batteryGetTemperature() =",
                myDroid.batteryGetTemperature()

            print "myDroid.batteryGetVoltage() =", myDroid.batteryGetVoltage()

            print "myDroid.readBatteryData() =", myDroid.readBatteryData()
            """

            #return 100  # int(myDroid.batteryGetLevel().result)

            # See http://www.mithril.com.au/android/doc/BatteryManagerFacade.html
            try:
                DebugPrint(
                    "GetBatteryLevelPercentage(): %s" % \
                    str(myDroid.batteryGetLevel()))

                """
                DebugPrint(
                    "GetBatteryLevelPercentage(): %d" % \
                    int(myDroid.batteryGetLevel().result))
                """

                DebugPrint(
                    "   %s" % \
                    str(myDroid.batteryCheckPresent()))

                DebugPrint(
                    "   %s" % \
                    str(myDroid.batteryGetHealth()))

                DebugPrint(
                    "   %s" % \
                    str(myDroid.batteryGetPlugType()))

                DebugPrint(
                    "   %s" % \
                    str(myDroid.batteryGetStatus()))

                DebugPrint(
                    "   %s" % \
                    str(myDroid.batteryGetTechnology()))

                DebugPrint(
                    "   %s" % \
                    str(myDroid.batteryGetTemperature()))

                DebugPrint(
                    "   %s" % \
                    str(myDroid.batteryGetVoltage()))

                DebugPrint(
                    "   %s" % \
                    str(myDroid.readBatteryData()))
            except:
                DebugPrintErrorTrace()
                return 100

            return 100

        elif SYMBIAN_S60_OS:
        # elif SYMBIAN_OS:
            # S60 3rd+ edition
            if S60_EDITION[0] >= 3:
                return int(sysinfo.battery())
            elif SYMBIAN_S60_2ND_ED:
                """
                On S60 2nd edition the max is 7 bars - see
                    http://discussion.forum.nokia.com/forum/showthread.php?97900-Is-there-a-way-to-show-battery-level-in-a-scale-from-0-to-99-instead-7preset-values
                """
                return int(sysinfo.battery() * 100.0 / 7.0)
        elif SYMBIAN_UIQ_OS:
            # else
            return 100
        elif iOS_PYOBJC:
            return 100
        elif WINDOWS_OS:
            return 100
        elif UNIX_OS:
            return 100
        elif WINDOWS_CE_OS_PYTHONCE:
            return 100
        elif RASPBIAN_OS:
            return 100
    except:
        DebugPrintErrorTrace()


def GetChargerStatusStr():
    global sysagentImported

    if ANDROID_OS:
        #print "myDroid.batteryGetStatus() =", myDroid.batteryGetStatus()
        #return "[N/A]"

        #return int(myDroid.batteryGetStatus().result)
        return str(myDroid.batteryGetStatus())
    elif SYMBIAN_S60_OS:
    # elif SYMBIAN_OS:
        if sysagentImported:
            try:
                res = int(sysagent.charger_status())
                if res == -1:
                    return "error"
                elif res == 0:
                    return "not connected"
                elif res == 1:
                    return "charging"
                elif res == 2:
                    return "not charging"
                elif res == 3:
                    return "almost done"
                elif res == 4:
                    return "charging done"
                elif res == 5:
                    return "charging cont'd"
                """
                EChargingStatusError              = -1,

                /// Charger not connected/uninitialized
                EChargingStatusNotConnected       = 0,

                /// Device is charging
                EChargingStatusCharging           = 1,

                /// Charger is connected, device not charging
                EChargingStatusNotCharging        = 2,

                /// Charging almost completed
                EChargingStatusAlmostComplete     = 3,

                /// Charging completed
                EChargingStatusChargingComplete   = 4,

                /// Charging continued after brief interruption
                EChargingStatusChargingContinued  = 5
                """
            except:
                DebugPrintErrorTrace()
                return "[N/A]"
        else:
            return "[N/A]"
    elif SYMBIAN_UIQ_OS:
        return "[N/A]"
    elif iOS_PYOBJC:
        return "[N/A]"
    elif WINDOWS_OS:
        return "[N/A]"
    elif WINDOWS_CE_OS_PYTHONCE:
        return "[N/A]"


def GetChargerStatus():
    global deviceId, sysagentImported

    if ANDROID_OS:
        # print "myDroid.batteryGetStatus() =", myDroid.batteryGetStatus()

        #return 1  # int(myDroid.batteryGetStatus().result)

        try:
            DebugPrint(
                "GetChargerStatus(): %s" % \
                str(myDroid.batteryGetStatus()))

            """
            DebugPrint(
                "GetChargerStatus(): %d" % \
                int(myDroid.batteryGetStatus().result))
            """
        except:
            DebugPrintErrorTrace()
            return 1

        return 1

    elif SYMBIAN_OS:
        """
        # Nokia 6680
        if deviceId == IMEI_6680:
            return 1
        """

        if sysagentImported:
            try:
                res = int(sysagent.charger_status())
                """
                EChargingStatusError              = -1,

                /// Charger not connected/uninitialized
                EChargingStatusNotConnected       = 0,

                /// Device is charging
                EChargingStatusCharging           = 1,

                /// Charger is connected, device not charging
                EChargingStatusNotCharging        = 2,

                /// Charging almost completed
                EChargingStatusAlmostComplete     = 3,

                /// Charging completed
                EChargingStatusChargingComplete   = 4,

                /// Charging continued after brief interruption
                EChargingStatusChargingContinued  = 5
                """
                return res
            except:
                DebugPrintErrorTrace()
                return 1
        else:
            return 1
    elif iOS_PYOBJC:
        return 1
    elif WINDOWS_OS:
        return 1
    elif UNIX_OS:
        return 1
    elif WINDOWS_CE_OS_PYTHONCE:
        return 1
    elif RASPBIAN_OS:
        return 1


def CallbackSMSSend(smsSendState):
    try:
        if smsSendState == messaging.ESent:
            DebugPrint("CallbackSMSSend(smsSendState = %d): " \
                            "Message was sent." % smsSendState)
        elif smsSendState == messaging.ESendFailed:
            DebugPrint("CallbackSMSSend(smsSendState = %d): Something went " \
                        "wrong while sending." % smsSendState)
    except:
        DebugPrintErrorTrace()


# TODO!!!! Read it from state.bin
lastTimeSentMessage = 0


def CommunicateWithOperator():
    global lastTimeSentMessage

    DebugPrint("Entered CommunicateWithOperator().")

    # MAX_TIME_BETWEEN_SEND_MESSAGES = 30 * 24 * 3600  # 30 days in seconds
    MAX_TIME_BETWEEN_SEND_MESSAGES = 1 * 24 * 3600   # 1 day in seconds

    try:
        if GetTime() - lastTimeSentMessage > MAX_TIME_BETWEEN_SEND_MESSAGES:
            DebugPrint("CommunicateWithOperator(): Sending SMS.")

            # This returns only if the SMS was sent successfully - the
            #   application sometimes gets blocked at .sms_sens()
            #messaging.sms_send("302", u"")

            messaging.sms_send("302", u"", "7bit", CallbackSMSSend)

            DebugPrint("Returned from messaging.sms_send().")

            lastTimeSentMessage = GetCurrentDateTime()
    except:
        DebugPrint("CommunicateWithOperator(): returned an exception.")
        DebugPrintErrorTrace()




# !!TODO - Maybe do separate Ops: MainCameraPhoto and MainCameraVideoRec
def MainCamera():
    res = 0
    # For the Main camera:
    if cameraMode[0] != 0:
        """
        if cameraMode[1] != 0:
            pauseIntervalCurrent = int(pauseInterval / 2)
            pauseIntervalLeft -= pauseIntervalCurrent
        """

        if (cameraMode[0] == 2) or (cameraMode[0] == 3):
            TakePhotoAndUpload(0)
            res = 1

        if (cameraMode[0] == 1) or (cameraMode[0] == 3):
            if videoRecordDuration[0] > 0:
                VideoRecordAndUpload(0,
                                    videoRecordDuration[0])
                res = 1

        """
        if pauseIntervalCurrent != 0:
            try:
                SleepAndPetWatchdog(pauseIntervalCurrent,
                                                    True)
            except:
                DebugPrint("ReactiveLoop(): sleep " \
                            "returned an exception.")
                DebugPrintErrorTrace()
        """
    return res


# !!TODO - Maybe do separate Ops: VGACameraPhoto and VGACameraVideoRec
def VGACamera():
    res = 0
    # For the VGA camera:
    if numCamerasSupported >= 2:
        if cameraMode[1] != 0:
            if (cameraMode[1] == 2) or (cameraMode[1] == 3):
                TakePhotoAndUpload(1)
                res = 1

            if (cameraMode[1] == 1) or (cameraMode[1] == 3):
                if videoRecordDuration[1] > 0:
                    VideoRecordAndUpload(1,
                            videoRecordDuration[1])
                    res = 1
    return res


def Audio():
    res = 0
    if audioRecordDuration != 0:
        AudioRecordAndUpload(audioRecordDuration)
        res = 1

    return res


"""
def Sleep():
    try:
        # e32.ao_sleep(pauseInterval)
        SleepAndPetWatchdog(pauseInterval, True)
        #!!!!TODO At every pet in SleepAndPetWatchdog we should increment a counter sleepState that is saved with StoreState() and in case iCam crashes and sleepState != -1 (??) we should resume the sleep.
        # SleepAndPetWatchdog(pauseIntervalLeft, True)
    except:
        # time.sleep(pauseInterval)
        DebugPrint("ReactiveLoop_real(): sleep returned an " \
                "exception.")
        DebugPrintErrorTrace()
"""


reactiveLoopOps = None
#if reactiveLoopOps is None:
reactiveLoopOps = [MainCamera, VGACamera, Audio]
#reactiveLoopOps = [MainCamera, VGACamera, Audio, Sleep]

# def StoreMediaAndUploadPeriodically():
def ReactiveLoop_real():
    """
    global deviceId
    global myTimer
    global localPhotoResolution, localQualityIndex
    global photoResolutionStr, photoModeStr, pauseIntervalStr, exposureStr
    global whiteBalanceStr, flashStr, digitalZoom, photoResolutionIndex
    global localPhotoResolutionIndex, photoModeIndex, photoQuality
    global pauseInterval, exposureIndex, whiteBalanceIndex, flashIndex
    global audioRecordDuration, videoRecordDuration, rotateDegreesImage

    # GSM location info
    global mobileCountryCode, mobileNetworkCode, locationAreaCode, cellId

    global signalStrength, signalUnits, accessPointName, bluetoothMode
    global gpsInfo, readGPS
    global cameraPhotoSizes_JPEG_Exif, cameraPhotoSizes_RGB24
    global myMaxRamdriveSize
    """

    # global keyboard
    # global myTimer
    global pauseIntervalStr, pauseInterval, videoRecordDuration, \
        audioRecordDuration, reactiveLoopIsStarted, burstModeIsStarted
    global modeManagerIsEnabled
    global readGPS
    global startButtonPressed
    global conserveEnergy
    global reactiveLoopOpsIndex, reactiveLoopOps

    DebugPrint("Entered ReactiveLoop_real().")

    try:
        if startButtonPressed == False:
            startButtonPressed = True

        if SYMBIAN_OS:
            RedrawHandler(None)

        if reactiveLoopIsStarted == False:
            burstModeIsStarted = False
            reactiveLoopIsStarted = True
            # We call SetMenu() in order to remove the Start menu item.
            SetMenu()

        """
        I used after() instead of forever loop with ao_sleep() because I've
            seen that probably because of the Internet connections to
            RDS prepaid the forever loop with ao_sleep() was broken and the
            application was no longer progress(??). That is why we use after(),
            which invariably progresses (it seems).

        But even the solution with after(), in conditions of stress - BT
            server with (own and BT client) pauseInterval = 120 had issues
            with no more progress.
        """
        # if True:
        while True:
            if reactiveLoopIsStarted:
                # EraseOldestMediaFiles()

                DebugPrint("Entered ReactiveLoop_real() iteration.")

                """
                hasDownloadedNewCmd = DownloadCommands()

                # DownloadCommands() returns true if reads new commands.
                if hasDownloadedNewCmd:
                    SetMenu()
                    StoreState()

                try:
                    #myTimer.cancel()
                    #myTimer.after(pauseInterval, ReactiveLoop)
                    #LongAfter(myTimer, pauseInterval + 40 + \
                    #   videoRecordDuration[0] + videoRecordDuration[1] + \
                    #   audioRecordDuration, ReactiveLoop)

                    global sleepAndPetWatchdogTimer
                    LongAfter(sleepAndPetWatchdogTimer, pauseInterval,
                        ReactiveLoop)
                except:
                    print "ReactiveLoop(): myTimer.after() returned an " \
                        "exception."
                    sys.stdout.flush()
                    traceback.print_exc()
                    sys.stderr.flush()

                if pauseInterval == 0:
                    return
                """

                if pauseInterval == 0:
                    #!!!!TODO: Test if this piece of code helps in case iCam crashes - it should.
                    if not burstModeIsStarted:
                        if (cameraMode[0] != 0) and (cameraMode[1] == 0):
                            # Burst mode photo
                            if cameraMode[0] == 2:
                                StartBurstMode()
                            else:
                                # Video rec
                                if videoRecordDuration[0] > 0:
                                    ContinuousVideoRecordAndUpload(0,
                                                        videoRecordDuration[0])
                        elif (cameraMode[0] == 0) and (cameraMode[1] != 0):
                            # Burst mode photo.
                            if cameraMode[1] == 2:
                                StartBurstMode()
                            else:
                                # Video rec
                                if videoRecordDuration[1] > 0:
                                    ContinuousVideoRecordAndUpload(1,
                                                        videoRecordDuration[1])
                    """
                    #It appears that if I give e32.ao_sleep() it actually makes
                    #   sleep the entire process, including the camera
                    #   ViewFinder (so the camera ViewFinder does not get
                    #   called very often).
                    try:
                        e32.ao_sleep(2) # [seconds]
                    except:
                        DebugPrint("ReactiveLoop(): ao_sleep() returned an " \
                                    "exception.")
                        DebugPrintErrorTrace()
                    """
                    reactiveLoopIsStarted = False

                    return
                else:
                    hasDownloadedNewCmd = DownloadCommands()

                    """
                    # DownloadCommands() returns true if reads new commands.
                    if hasDownloadedNewCmd:
                        SetMenu()
                        StoreState()
                    """

                    """
                    This is a tentative implementation of a "watchdog" directly
                        inside iCam as well.
                    Especially for Nokia 6680 I appreciate being less likely
                       for iCam to simply crash - but it can run out of memory.
                    """
                    FREE_RAM_AMOUNT_THRESHOLD = 1 * MEGA_BYTE

                    if GetFreeRAM() < FREE_RAM_AMOUNT_THRESHOLD:
                        DebugPrint("ReactiveLoop_real(): Free RAM is %d - " \
                                "below %d. Restarting cellphone." % \
                                (GetFreeRAM(), FREE_RAM_AMOUNT_THRESHOLD))

                        RestartPhone()

                    """
                    I do this here, because I want to resize the photos
                        received via BT with the latest resolution received
                        from DownloadCommands().
                    I should put the BluetoothMessageListProcess() before
                        calling DownloadCommands(), since I want to concentrate
                        all Internet access close, since
                        BluetoothMessageListProcess() takes a lot of time.
                    """
                    if (bluetoothMode == 1) or (bluetoothMode == 2):
                        # This might take a long time to complete, if there are
                        #   many BT messages in Inbox.
                        #BluetoothMessageListProcess(processJustNonSMF_BtMsgs=\
                        #                                                False)
                        BluetoothMessageListProcess()
                        """
                        Here we come back after a
                          SleepAndPetWatchdog(pauseIntervalGdata) -
                          which is good since normally we have immediately
                          after (see below) an UploadStateAndFileAndStoreState.
                        """

                    """
                    if modeManagerIsEnabled:
                        ModeManager()

                    PowerManager()
                    """

                    # Even if we don't capture anything we still transmit the
                    #   device's state (GPS if available, GSM, charghing
                    #   status, free drive space, etc, etc)
                    if (cameraMode[0] == 0) and (cameraMode[1] == 0) and \
                            (audioRecordDuration == 0):  # and readGPS):
                        UploadStateAndFileAndStoreState(deviceId, -1, #cameraId
                                NO_MEDIA_FILE_NAME, NO_MEDIA_FILE_NAME,
                                ICAM_SERVER_NAME,
                                WEBPAGE_UL_GZIPPED_STATE_AND_FILE)

                    """
                    # We want to distribute the pauseInterval between the
                    #   cameras, etc.
                    pauseIntervalCurrent = 0
                    pauseIntervalLeft = pauseInterval
                    """

                    """
                    In the loop below
                            while finishLoop == 0:
                        we iterate until we successfully complete an operation
                        (really take a photo, record a video or audio).

                      Note that we have an outer forever loop.
                    """
                    finishLoop = 0
                    while finishLoop == 0:
                        DebugPrint("ReactiveLoop_real(): " \
                                    "reactiveLoopOpsIndex=%d, " \
                                    "reactiveLoopOps=%s." % \
                                    (reactiveLoopOpsIndex, reactiveLoopOps))

                        if (reactiveLoopOpsIndex >= len(reactiveLoopOps)) or \
                                                    (reactiveLoopOpsIndex < 0):
                            reactiveLoopOpsIndex = 0

                        """
                        We act a bit paranoid here, in the sense that we want
                            to ensure progress, even if the current operation
                            crashes in the middle or (best) at the very end.
                          Independent of what happens at the current operation
                            we proceed at the next one - we have experienced
                            a few times already when the operation was crashing
                            (main thread was not progressing) after we have
                            uploaded the media via Bluetooth to the iCam BT
                            server.
                        """
                        prevReactiveLoopOpsIndex = reactiveLoopOpsIndex

                        # We update reactiveLoopOpsIndex.
                        reactiveLoopOpsIndex += 1
                        if reactiveLoopOpsIndex >= len(reactiveLoopOps):
                            reactiveLoopOpsIndex = 0

                        """
                        We give StoreState() to quickly save the updated
                            reactiveLoopOpsIndex.
                        """
                        StoreState()

                        """
                        The operation (MainCamera, VGACamera, Audio) returns 1
                          if the operation did something concrete (take photo
                          or video recording) or 0 if, given the current iCam
                          settings, did not basically do anything.
                        """
                        finishLoop = reactiveLoopOps[prevReactiveLoopOpsIndex]()

                    """
                    # The straight/non-paranoid alternative:

                    reactiveLoopOps[reactiveLoopOpsIndex]()

                    # We update reactiveLoopOpsIndex.
                    reactiveLoopOpsIndex += 1
                    if reactiveLoopOpsIndex >= len(reactiveLoopOps):
                        reactiveLoopOpsIndex = 0

                    # We give StoreState() to quickly save the updated
                    #    reactiveLoopOpsIndex.
                    StoreState()
                    """


                    """
                    In case more Unsent files are available because of
                        transmission errors.
                    """
                    if (uploadUnsentData == 1) or (uploadUnsentData == 3):
                        UploadUnsentFILES()

                    try:
                        # e32.ao_sleep(pauseInterval)
                        SleepAndPetWatchdog(pauseInterval, True)
                        # SleepAndPetWatchdog(pauseIntervalLeft, True)
                    except:
                        # time.sleep(pauseInterval)
                        DebugPrint("ReactiveLoop_real(): sleep returned an " \
                                    "exception.")

                        DebugPrintErrorTrace()

                    DebugPrint("Exiting ReactiveLoop_real().")
            else:
            #if reactiveLoopIsStarted == False:
                # We should get here only on Android OS.
                if ANDROID_OS:
                    """
                    try:
                        time.sleep(10.0)
                    except:
                        DebugPrint("ReactiveLoop_real(): while " \
                                "reactiveLoopIsStarted=False, time.sleep() " \
                                "returned an exception.")
                        DebugPrintErrorTrace()
                    """
                    DisplayNote("Waiting to start.")
                    SleepAndPetWatchdog(10.0, False)
                elif SYMBIAN_OS:
                    return
    except:
        DebugPrintErrorTrace()


def ReactiveLoop():
    ReactiveLoop_real()
    #thread.start_new_thread(ReactiveLoop_real, ())
    #MyThreadStart(ReactiveLoop_real)


def StopBroadcasting():
    global reactiveLoopIsStarted
    global startButtonPressed

    if SYMBIAN_OS:
        # Before and after camera.start_record(), videoRecordStartTime == -1 .
        # if videoRecordTime != 0: #or (videoRecordStartTime == -1)

        # Before and after camera.start_record(), videoRecordStartTime == -1 .
        if videoRecordStartTime == -1:  # or (videoRecordStartTime == -1)
            reactiveLoopIsStarted = False
            startButtonPressed = False

            # We call SetMenu() in order to remove the Start menu item.
            SetMenu()

            #!!!!Why do we do this?
            # Nokia E7 and N82.
            if deviceId in [IMEI_E7, IMEI_N82]:
                while True:
                    if reactiveLoopIsStarted:
                        break
                    SleepAndPetWatchdog(MAX_DURATION_BETWEEN_PETTING, False)
    elif ANDROID_OS:
        reactiveLoopIsStarted = False
        startButtonPressed = False


def ReactiveLoopOnlyIfStartButtonNotAlreadyPressed():
    global startButtonPressed
    global reactiveLoopIsStarted

    if ANDROID_OS:
        """
        Normally, when we have startButtonPressed == True, we call
            ReactiveLoop() in MainStartReactiveLoops(). I aggree that
            startButtonPressed and reactiveLoopIsStarted are almost the same.
        """
        if startButtonPressed:
            #myDroid.makeToast("Pressed start.")
            #time.sleep(2.0)

            if reactiveLoopIsStarted:
                DisplayNote("Already started.")
            else:
                DisplayNote("Restarting.")
                # We have already given at least once Start and then a Stop.
                reactiveLoopIsStarted = True
        else:
            DisplayNote("Starting.")
            ReactiveLoop()
    elif SYMBIAN_OS:
        ReactiveLoop()
    elif WINDOWS_CE_OS_PYTHONCE:
        ReactiveLoop()


def ReactiveLoopStop():
    global reactiveLoopIsStarted, startButtonPressed

    reactiveLoopIsStarted = False
    # startButtonPressed = False

    if ANDROID_OS:
        pass
        # DisplayNote("Stopped.")


if ANDROID_OS:
    myAllowReadEvents = True

    def EventHandler():
        global myAllowReadEvents

        DebugPrint("Entered EventHandler().")

        while True:
            """
            try:
                # Returns information about the currently active access point.
                print "EventHandler(): myDroid.wifiGetConnectionInfo() =", \
                        myDroid.wifiGetConnectionInfo()

                # Reassociates with the currently active access point.
                #   Returns: True if the operation succeeded.
                #myDroid.wifiReassociate()

                # Reconnects to the currently active access point.
                #   Returns: True if the operation succeeded.
                myDroid.wifiReconnect()
            except:
                DebugPrintErrorTrace()
            """
            # e = myDroid.receiveEvent()

            """
            See related posts:
              https://groups.google.com/group/android-scripting/msg/c9e805c4937f2d5e
              https://groups.google.com/group/android-scripting/browse_thread/thread/ae658a62b2b60a30
            """
            NEW_STYLE = False

            try:
                if myAllowReadEvents:
                    if NEW_STYLE == False:
                        e = myDroid.eventPoll(1)
                    elif NEW_STYLE == True:
                        """
                        It seems that sometimes it consumes events that are
                            generated by the RPC system, which implies it
                            sabotages the communication between the Python
                            program and the SL4A Java library.
                        """
                        e = myDroid.eventWait(10000)

                    # DisplayNote("read event!")
                    if e.result is None:
                        pass
                    else:
                        #DebugPrint("EventHandler(): e = %s" % str(e))

                        """
                        #!!!!Check for events generated by cameraStartPreview:
                            EventHandler(): e = Result(id=169, result=[{u'data': {u'format': u'jpeg', u'encoding': u'file', u'height': 288, u'width': 352, u
                        'filename': u'/mnt/sdcard/external_sd/iCam/Media/prv-1287331584.jpg', u'quality': 80}, u'name': u'preview', u'time': 1377247976378000L}], error=None)
                        """

                        """
                        DebugPrint("EventHandler(): e.result['name'] = %s" % \
                                    str(e.result["name"]))

                        DebugPrint("EventHandler(): " \
                                    "optItems[e.result['name']] = %s" % \
                                    str(optItems[str(e.result["name"])]))

                        DebugPrint("EventHandler(): e.result['data'] = %s" % \
                                    str(e.result["data"]))
                        """

                        #optItems[str(e.result["name"])]["event"](e.result["data"])
                        try:
                            # True and False are both bool and int
                            #   - see http://www.peterbe.com/plog/bool-is-int.
                            if isinstance(e.result, bool):
                                DebugPrint(
                                    "EventHandler(): isinstance(e.result) is " \
                                    "bool")
                                #pass
                            elif isinstance(e.result, int):
                                DebugPrint(
                                    "EventHandler(): isinstance(e.result) is " \
                                    "int")
                                #pass
                            elif isinstance(e.result, list):
                                DebugPrint(
                                    "EventHandler(): len(e.result) = %d" % \
                                    len(e.result))

                                if len(e.result) == 1:
                                    if NEW_STYLE == False:
                                        myMap = e.result[0]

                                    if NEW_STYLE == True:
                                        myMap = e.result

                                    # myMap = e.result
                                    if isinstance(myMap, bool):
                                        DebugPrint(
                                            "EventHandler(): myMap is bool: %s" % \
                                            str(myMap))
                                    elif isinstance(myMap, int):
                                        DebugPrint(
                                            "EventHandler(): myMap is int: %s" % \
                                            str(myMap))
                                    elif (u"name" in myMap) and \
                                                    (u"data" in myMap) and \
                                                    (myMap[u"data"] is None):
                                        DebugPrint("EventHandler(): e = %s" % \
                                                    str(e))
                                        """
                                        IMPORTANT: Here we treat the events
                                            related to the SL4A UI menu.
                                        """

                                        # menuTable[str(myMap["name"])][1]()
                                        """
                                        thread.start_new_thread(menuTable[
                                                str(myMap["name"])][1], ())
                                        """
                                        MyThreadStart( \
                                            menuTable[str(myMap[u"name"])][1])
                                    # (myMap[u"format"] == u"jpeg") and
                                    elif (u"data" in myMap[u"data"]) and \
                                                (myMap[u"data"] != None) and \
                                                (u"format" in myMap[u"data"]):
                                        """
                                        This is an event containing the frame info
                                            data in Base64 from cameraStartPreview().
                                        """

                                        frameData = myMap[u"data"][u"data"]
                                        frameData = base64.b64decode(frameData)

                                        #!!!!TODO: get right cameraId
                                        cameraId = 0

                                        photoFileName = \
                                            GetCurrentDateTimeStringWithMilliseconds() + \
                                            "_%s_%d_burst.jpg" % (deviceId, cameraId)
                                            #"_%d.jpg" % cameraId

                                        crtTime = GetCurrentDateTime()
                                        crtTime2 = GetTime()
                                        # See http://discussion.forum.nokia.com/forum/showthread.php?116978-What-is-the-time-granularity-in-Pys60 .
                                        numMilliseconds = (crtTime2 - int(crtTime2)) * 1000

                                        PicasaPhotoUpload(
                                            pathFileName="/bogus/bogus.jpg",
                                            fileName=photoFileName,
                                            aKeyword=googleKeywords,
                                            crtTime=crtTime,
                                            mediaTimeStr=str(int(numMilliseconds)),
                                            mediaDateStr="",
                                            aDeviceId=deviceId,
                                            cameraId=0,
                                            aBatteryLevel=-1,
                                            aChargerStatus=-1,
                                            aData=frameData)
                                    else:
                                        DebugPrint("EventHandler(): e = %s" % \
                                                    str(e))
                        except:
                            DebugPrintErrorTrace()

                    # myDroid.creaMenu()
                    if NEW_STYLE == False:
                        time.sleep(2.0)
            except:
                # time.sleep(0.2)
                DebugPrintErrorTrace()


# This var is required as a global.
serversFormSaved = False

def SelectServersMenu():
    global serversFormSaved
    global googleUsername, googlePassword, googleRememberPassword
    global uploadMediaToYouTube, uploadMediaToPicasa, \
        useiCamServer, googleMediaPrivate, googleKeywords #, btNetSearchKeywords
    global ICAM_SERVER_NAME

    DebugPrint("Entered SelectServersMenu().")

    """
    From http://wiki.forum.nokia.com/index.php/How_to_use_Form_in_Python_for_S60
        (and also http://www.mobilenin.com/pys60/info_tabs_forms.htm):
    """
    if googleUsername is None:
        googleUsername = ""
    if googlePassword is None:
        googlePassword = ""

    # Create a list to be used in "combo" selection mode.
    iCamComboList = [u"No", u"Yes, No Media", u"Yes, All"]
    yesNoComboList = [u"No", u"Yes"]
    noComboList = [u"No"]
    infoComboList = [u"[Enter When Save Form]"]

    """
    uploadMediaToYouTubeComboList = [u"No", u"Yes"]
    uploadMediaToPicasaComboList  = [u"No", u"Yes"]
    googleRememberPasswordComboList = [u"No", u"Yes"]
    """
    googleMediaPrivateComboList = [u"Public (shared to all)",
                                   u"Private (shared only explicitely)"]

    myFields = [
            #0
            #(u"Enter Google YouTube/Picasa login information", "text", u""),
            (u"YouTube/Picasa username", "text", unicode(googleUsername)),

            #1
            # Enter On Save Form
            #(u"YouTube/Picasa password", "text", u"[Enter When Save Form]"),
            #unicode(GetGooglePassword())),
            (u"YouTube/Picasa password", "combo", (infoComboList, 0)),

            #2
            (u"Remember password", "combo",
                (yesNoComboList, googleRememberPassword)),
            # Gives error: "ValueError: Form field, unknown type".
            #(u"Password", "code", unicode(googlePassword)),

            #3
            (u"Upload to YouTube", "combo",
                (yesNoComboList, uploadMediaToYouTube)),

            #4
            (u"Upload to Picasa", "combo",
                (yesNoComboList, uploadMediaToPicasa)),

            #5
            # The iCam server is not for use for normal users ;)
            #(u"Upload to iCam server", "combo",
            #   (yesNoComboList, useiCamServer)),
            #(u"Upload media to iCam Server", "combo",
            (u"Use the iCam Server", "combo",
                (noComboList, useiCamServer)),

            #6
            (u"Media permissions on YouTube/Picasa", "combo",
                (googleMediaPrivateComboList, googleMediaPrivate)),

            #7
            (u"YouTube video keywords", "text", unicode(googleKeywords)),

            #8
            (u"iCam Server address", "text", unicode(ICAM_SERVER_NAME))
            #(u"Permissions of uploaded media on YouTube/Picasa:",
            #   "combo", (googleMediaPrivateComboList, 0))

            #(u"Amount", "number", 5),
            #(u"Date", "date"),
            #(u"Time","time")
        ]

    # Nokia E7 and 6120 and 6680 and N95 and N82
    if deviceId in [IMEI_E7, IMEI_6120, IMEI_6680, IMEI_N95, IMEI_N82]:
        myFields[5] = (u"Use the iCam Server", "combo",
                       #(yesNoComboList, useiCamServer))
                       (iCamComboList, useiCamServer))

    """
    if MY_DEBUG_STDOUT:
        try:
            print "SelectServersMenu(), at the beginning:"
            print "    googleUsername =", googleUsername
            print "    googlePassword =", googlePassword
            print "    googleRememberPassword =", googleRememberPassword
            print "    uploadMediaToYouTube =", uploadMediaToYouTube
            print "    uploadMediaToPicasa =", uploadMediaToPicasa
            print "    googleMediaPrivate =", googleMediaPrivate
            print "    googleKeywords =", googleKeywords
            print "    myFields =", myFields
            sys.stdout.flush()
        except:
            DebugPrintErrorTrace()
    """

    if ANDROID_OS:
        """
        Sort-of-TODO: We get decalated results if we put these here:
            username is returned by password . This is an error of the
            SL4A event handling, RPC, etc.

        See Z:\1PhD\ReVival\Logs\Samsung_i5500\2013_12_15\iCam.cfg
          Entered GetGooglePassword().
          LoadLocalConfigFromFile(): googlePasswordAux = googleUser.
          ..
          SelectServersMenu(): at the end
          googleUsername = [1]

        So we read uploadMediaToYouTube uploadMediaToPicasa after 
            googleUsername and googlePassword.
        """
        if False:
            uploadMediaToYouTube = DialogMultipleChoices( \
                myFields[3][0] + u"?", yesNoComboList, uploadMediaToYouTube)

            uploadMediaToPicasa = DialogMultipleChoices( \
                myFields[4][0] + u"?", yesNoComboList, uploadMediaToPicasa)

        """
        useiCamServer = DialogMultipleChoices( \
                myFields[5][0] + u"?", myFields[5][2][0], useiCamServer)
        """

        try:
            googleUsername = DialogGetInput(myFields[0][0] + ":", "", \
                                            googleUsername)

            googlePassword = \
                myDroid.dialogGetPassword(myFields[1][0] + ":", "").result
                # "For " + googleUsername
            if googlePassword is None:
                googlePassword = ""

            StoreLocalConfigInFile()
        except:
            DebugPrintErrorTrace()

        if True: # See above
            uploadMediaToYouTube = DialogMultipleChoices( \
                myFields[3][0] + u"?", yesNoComboList, uploadMediaToYouTube)

            uploadMediaToPicasa = DialogMultipleChoices( \
                myFields[4][0] + u"?", yesNoComboList, uploadMediaToPicasa)

        googleMediaPrivate = DialogMultipleChoices(myFields[6][0], \
                                myFields[6][2][0], googleMediaPrivate)

        googleKeywords = DialogGetInput(myFields[7][0] + ":", "",
                                        googleKeywords)

    elif SYMBIAN_OS:
        try:
            # Initialize a boolean variable to know whether the form is saved.
            serversFormSaved = False

            # appuifw.app.title = u"YouTube/Picasa".
            appuifw.app.title = u"Select Servers"

            # Creates the form:
            #serversForm = appuifw.Form(myFields, flags = appuifw.FFormEditModeOnly)
            #serversForm = appuifw.Form(myFields, appuifw.FFormEditModeOnly)
            #serversForm = appuifw.Form(myFields, appuifw.FFormDoubleSpaced)
            serversForm = appuifw.Form(myFields, appuifw.FFormEditModeOnly |
                                                 appuifw.FFormDoubleSpaced)

            # Define a function to be called when the form is saved.
            def ServersFormOnSave(arg):
                global serversFormSaved
                global googlePassword
                try:
                    serversFormSaved = True

                    myAnswer = appuifw.query(u"Enter YouTube/Picasa " \
                                    "password? WARNING: password will be " \
                                    "visible when edited.", "query")
                    """
                    DebugPrint("SelectServersMenu(): myAnswer = %s" % \
                                str(myAnswer))
                    """

                    # If press Cancel, myAnswer is None.
                    if myAnswer == True:
                        resStr = appuifw.query(
                                u"Enter YouTube/Picasa password:",
                                "text", unicode(GetGooglePassword())) # "code"

                        if resStr is not None:
                            googlePassword = str(resStr)
                except:
                    DebugPrintErrorTrace()

                return True

            # Assign the save function.
            serversForm.save_hook = ServersFormOnSave

            """
            From http://discussion.forum.nokia.com/forum/showthread.php?137062-How-to-avoid-quot-Save-quot-command-in-appuifw.Form-%28Make-quot-Auto-Save-quot-form%29
            """
            #serversForm.menu = [(u"Enter Password", ServersFormOnSave)]
            #"My Save" "Test Login Google"

            #Show the form. This operation is blocking until we close the form.
            serversForm.execute()

            # After the form is saved and closed, display the information.
            if serversFormSaved == True:
                """
                print serversForm[0][2]
                print models[serversForm[1][2][1]]
                print serversForm[2][2]
                print time.strftime("%d/%m/%Y",
                        time.localtime(serversForm[3][2]))
                print time.strftime(time.ctime(serversForm[4][2])[11:20])
                """

                """
                appuifw.note(unicode("Google Username: " +
                                    serversForm[0][2]), "info")

                appuifw.note(unicode("Google Password: " +
                                    serversForm[1][2]), "info")

                appuifw.note(unicode("YouTube: " +
                    uploadMediaToYouTubeComboList[serversForm[2][2][1]]),
                    "info")

                appuifw.note(unicode("Picasa: " +
                    uploadMediaToPicasaComboList[serversForm[3][2][1]]),
                    "info")

                appuifw.note(unicode("YouTube/Picasa: " + serversForm[4][2]),
                    "info")
                """

                googleUsername = serversForm[0][2]
                # googlePassword = serversForm[1][2]

                """
                The combo form field value is a long integer. We convert it to
                    int because we would receive
                    "TypeError: Form combo field, bad index"
                    at the next instantiation of appuifw.Form().
                """
                googleRememberPassword = int(serversForm[2][2][1])
                uploadMediaToYouTube = int(serversForm[3][2][1])
                uploadMediaToPicasa = int(serversForm[4][2][1])
                useiCamServer = int(serversForm[5][2][1])
                googleMediaPrivate = int(serversForm[6][2][1])
                googleKeywords = serversForm[7][2]

                #!!!!TODO
                #btNetSearchKeywords = serversForm[7][2]

                # It seems serversForm[2][2] is unicode
                ICAM_SERVER_NAME = str(serversForm[8][2])

                StoreLocalConfigInFile()
                StoreState()
        except:
            DebugPrintErrorTrace()
        """
        # Does not work in Python 2.2 (e.g., PyS60 1.4.5)
        finally:
            appuifw.app.title = ICAM_APP_TITLE
        """
        appuifw.app.title = ICAM_APP_TITLE

    # """
    if MY_DEBUG_STDOUT:
        try:
            print "SelectServersMenu(): at the end"
            print "    googleUsername =", googleUsername
            # print "    googlePassword =", googlePassword
            print "    googleRememberPassword =", googleRememberPassword
            print "    uploadMediaToYouTube =", uploadMediaToYouTube
            print "    uploadMediaToPicasa =", uploadMediaToPicasa
            print "    useiCamServer =", useiCamServer
            print "    googleMediaPrivate =", googleMediaPrivate
            print "    googleKeywords =", googleKeywords
            sys.stdout.flush()
        except:
            DebugPrintErrorTrace()
    # """



# This var is required as a global.
miscFormSaved = False

def MiscellaneousSettingsMenu():
    global miscFormSaved
    global startAutomatically, readGPS, storeLocallyMedia, LOCAL_FOLDER
    global numThreadsUpload
    global BATTERY_LEVEL_THRESHOLD
    global MY_DEBUG_STDOUT, MY_DEBUG_STDERR, MY_DEBUG_STDERR_2, \
                                            MY_DEBUG_UPLOAD_MSG

    DebugPrint("Entered MiscellaneousSettingsMenu().")

    # Create a list to be used in "combo" selection mode.
    yesNoComboList = [u"No", u"Yes"]
    noComboList = [u"No"]

    mask = (MY_DEBUG_STDOUT << 3) + (MY_DEBUG_STDERR << 2) + \
            (MY_DEBUG_STDERR_2 << 1) + MY_DEBUG_UPLOAD_MSG

    myFields = [
                (u"Start broadcasting automatically", "combo",
                    (yesNoComboList, startAutomatically)),

                #u"Media Storage"
                (u"Store Locally", "combo",
                    (yesNoComboList, storeLocallyMedia)),

                (u"iCam Folder", "text", unicode(LOCAL_FOLDER)),

                (u"Number of upload threads for burst mode", "number",
                    numThreadsUpload),

                (u"Battery level threshold % (for PM)", "number",
                    BATTERY_LEVEL_THRESHOLD),

                #(u"Debug logging (all; mask=%d)" % mask, "combo", (yesNoComboList,
                (u"Debug logging (all)", "combo", (yesNoComboList,
                        MY_DEBUG_STDOUT or MY_DEBUG_STDERR or
                        MY_DEBUG_STDERR_2 or MY_DEBUG_UPLOAD_MSG))
                # add Log: MY_DEBUG_*!!!!
        ]

    if ANDROID_OS:
        try:
            startAutomatically = DialogMultipleChoices( \
                myFields[0][0] + u"?", yesNoComboList, int(startAutomatically))

            storeLocallyMediaStrList = [ u"Do not store media", \
                                            u"Store all media" ]
            storeLocallyMedia = DialogMultipleChoices(myFields[1][0], \
                            storeLocallyMediaStrList, int(storeLocallyMedia))

            StoreState()
        except:
            # StoreLocalConfigInFile()
            DebugPrintErrorTrace()
    elif SYMBIAN_OS:
        try:
            # Initialize a boolean variable to know whether the form is saved.
            miscFormSaved = False

            # appuifw.app.title = u"YouTube/Picasa"
            appuifw.app.title = u"Misc"

            # Creates the form
            """
            #define KFormEditModeOnly 0x0001
            #define KFormViewModeOnly 0x0002
            #define KFormAutoLabelEdit 0x0004
            #define KFormAutoFormEdit 0x0008
            #define KFormDoubleSpaced 0x0010

            # miscForm = appuifw.Form(myFields,
            #       flags = appuifw.FFormEditModeOnly)

            # miscForm = appuifw.Form(myFields,
            #       appuifw.FFormEditModeOnly)
            """
            miscForm = appuifw.Form(myFields, appuifw.FFormEditModeOnly | \
                                                appuifw.FFormDoubleSpaced)

            # Define a function to be called when the form is saved.
            def MiscFormOnSave(arg):
                global miscFormSaved
                miscFormSaved = True
                return True

            # Assign the save function.
            miscForm.save_hook = MiscFormOnSave

            #Show the form. This operation is blocking until we close the form.
            miscForm.execute()

            # After the form is saved and closed, display the information.
            if miscFormSaved == True:
                """
                The combo form field value is a long integer.
                    We convert it to int because we would receive
                    "TypeError: Form combo field, bad index"
                    at the next instantiation of appuifw.Form().
                """
                startAutomatically = int(miscForm[0][2][1])

                storeLocallyMedia = int(miscForm[1][2][1])

                if LOCAL_FOLDER != str(miscForm[2][2]):
                    # It seems miscForm[2][2] is unicode
                    LOCAL_FOLDER = str(miscForm[2][2])

                    DebugPrint("MiscellaneousSettingsMenu(): set " \
                                "LOCAL_FOLDER = %s" % LOCAL_FOLDER)

                    global LOCAL_FOLDER_MEDIA_FILES, LOCAL_FOLDER_UNSENT_FILES

                    LOCAL_FOLDER_MEDIA_FILES = LOCAL_FOLDER + "/Media"
                    LOCAL_FOLDER_UNSENT_FILES = LOCAL_FOLDER + "/Unsent"
                    CreateDirectoriesAndLogFiles()

                numThreadsUpload = int(miscForm[3][2])

                BATTERY_LEVEL_THRESHOLD = int(miscForm[4][2])

                MY_DEBUG_STDOUT = int(miscForm[5][2][1])
                MY_DEBUG_STDERR = int(miscForm[5][2][1])
                MY_DEBUG_STDERR_2 = int(miscForm[5][2][1])
                MY_DEBUG_UPLOAD_MSG = int(miscForm[5][2][1])

                StoreState()
        except:
            #StoreLocalConfigInFile()
            DebugPrintErrorTrace()
        """
        # Doesn't work in Python 2.2 (e.g., PyS60 1.4.5).
        finally:
            appuifw.app.title = ICAM_APP_TITLE
        """
        appuifw.app.title = ICAM_APP_TITLE

    # """
    if MY_DEBUG_STDOUT:
        try:
            print "MiscellaneousSettingsMenu(): at the end"
            print "    startAutomatically =", startAutomatically
            print "    readGPS =", readGPS
            sys.stdout.flush()
        except:
            DebugPrintErrorTrace()
    # """


# This var is required as a global.
analysisFormSaved = False

def MediaAnalysisMenu():
    global analysisFormSaved
    # global burstModeIsStarted
    global motionDetectionIsOn

    # Create a list to be used in "combo" selection mode
    yesNoComboList = [u"No", u"Yes"]
    noComboList = [u"No"]

    myFields = [(u"Use Motion Detection for Photo Burst Mode", "combo",
                (yesNoComboList, motionDetectionIsOn))]

    if ANDROID_OS:
        try:
            pass
        except:
            DebugPrintErrorTrace()
    elif SYMBIAN_OS:
        try:
            # Initialize a boolean variable to know whether the form is saved.
            analysisFormSaved = False

            #appuifw.app.title = u"Motion Detection"
            appuifw.app.title = u"Media Analysis"

            # Creates the form
            #analysisForm = appuifw.Form(myFields,
            #   flags = appuifw.FFormEditModeOnly)

            #analysisForm = appuifw.Form(myFields, appuifw.FFormEditModeOnly)
            #analysisForm = appuifw.Form(myFields, appuifw.FFormDoubleSpaced)
            analysisForm = appuifw.Form(myFields, appuifw.FFormEditModeOnly |
                                                  appuifw.FFormDoubleSpaced)

            # Define a function to be called when the form is saved.
            def AnalysisFormOnSave(arg):
                global analysisFormSaved
                analysisFormSaved = True

                return True

            # Assign the save function.
            analysisForm.save_hook = AnalysisFormOnSave

            #Show the form. This operation is blocking until we close the form.
            analysisForm.execute()

            # After the form is saved and closed, display the information.
            if analysisFormSaved == True:
                """
                The combo form field value is a long integer.
                    We convert it to int because we would receive
                    "TypeError: Form combo field, bad index"
                    at the next instantiation of appuifw.Form().
                """
                motionDetectionIsOn = int(analysisForm[0][2][1])

                # viewFinderSize = (80, 60)
                # photoResolutionIndex = 2 #Why?
                # SetUploadedPhotoResolutionIndex(2)

                DebugPrint("MediaAnalysisMenu(): motionDetectionIsOn = %d" % \
                            motionDetectionIsOn)

                StoreState()
        except:
            # StoreLocalConfigInFile()
            DebugPrintErrorTrace()
        """
        # Doesn't work in Python 2.2 (e.g., PyS60 1.4.5).
        finally:
            appuifw.app.title = ICAM_APP_TITLE
        """
        appuifw.app.title = ICAM_APP_TITLE


"""
PyS60 menu:
    Start Broadcasting
    Select Preset Mode
        Main - Video Record
        Main - Take Photo
        Main - Burst Photos
        Main - Burst Videos
    Capture What
    Viewfinder
    Settings
        Select Servers
        Select Access Point
        Record Config
        Photo Config
        Pause Interval
        #Motion Detection
        Media Analysis
        Misc
        # Select BT Mode (Maybe call it Communication/Networking Config)
        Bluetooth Intranet
    Display Info
    Help
    Exit
"""


def SelectPresetMainVideoRecord():
    global cameraMode, videoRecordDuration, readGPS, pauseInterval, \
        localPhotoResolutionIndex, photoResolutionIndex
    global videoAudioEnabled, localVideoModeIndex, storeLocallyMedia

    cameraMode[0] = 1
    cameraMode[1] = 0
    #videoRecordDuration[0] = 30
    readGPS = 0
    pauseInterval = 120
    # localPhotoResolutionIndex[0] = 0

    # "Use Local Resolution"
    # photoResolutionIndex = 1
    videoAudioEnabled = 1

    # localVideoModeIndex = !!!!
    storeLocallyMedia = 1

    StoreState()


def SelectPresetMainTakePhoto():
    global cameraMode, videoRecordDuration, readGPS, pauseInterval, \
        localPhotoResolutionIndex, photoResolutionIndex
    global videoAudioEnabled, localVideoModeIndex, storeLocallyMedia

    cameraMode[0] = 2
    cameraMode[1] = 0
    # videoRecordDuration[0] = 30
    readGPS = 0
    pauseInterval = 120

    # localPhotoResolutionIndex[0] = 0
    # "Use Local Resolution"
    photoResolutionIndex = 1

    videoAudioEnabled = 1
    # localVideoModeIndex = !!!!

    storeLocallyMedia = 1

    StoreState()


def SelectPresetMainBurstPhotos():
    global cameraMode, videoRecordDuration, readGPS, pauseInterval, \
        localPhotoResolutionIndex, photoResolutionIndex
    global videoAudioEnabled, localVideoModeIndex, storeLocallyMedia
    global uploadMediaToYouTube, uploadMediaToPicasa, useiCamServer

    cameraMode[0] = 2
    cameraMode[1] = 0

    # videoRecordDuration[0] = 30
    readGPS = 0
    pauseInterval = 0

    # localPhotoResolutionIndex[0] = 0
    photoResolutionIndex = 4  # 160x120; 6 = 320x240

    # videoAudioEnabled = 1
    # localVideoModeIndex = !!!!

    # storeLocallyMedia = 1

    """
    #if (deviceId == IMEI_N82) or (deviceId == IMEI_E7):
    uploadMediaToYouTube = 0
    uploadMediaToPicasa = 0
    useiCamServer = 2
    """
    StoreLocalConfigInFile()
    StoreState()


def SelectPresetMainBurstVideos():
    global cameraMode, videoRecordDuration, readGPS, pauseInterval, \
        localPhotoResolutionIndex, photoResolutionIndex
    global videoAudioEnabled, localVideoModeIndex, storeLocallyMedia
    global uploadMediaToYouTube, uploadMediaToPicasa, useiCamServer

    cameraMode[0] = 1
    cameraMode[1] = 0

    # videoRecordDuration[0] = 30
    readGPS = 0
    pauseInterval = 0
    # localPhotoResolutionIndex[0] = 0
    # photoResolutionIndex = 4 #160x120; 6 = 320x240
    # videoAudioEnabled = 1
    # localVideoModeIndex = !!!!

    # storeLocallyMedia = 1

    """
    #if (deviceId == IMEI_N82) or (deviceId == IMEI_E7):
    uploadMediaToYouTube = 0
    uploadMediaToPicasa = 0
    useiCamServer = 2
    """

    StoreLocalConfigInFile()
    StoreState()


def SetCameraParametersMenu():
    if SYMBIAN_OS:
        """
        /** Specifies the type of exposure. - EExposureAuto is the default
            value. */
        enum TExposure
            {
            /** Set exposure automatically. Default, always supported. */
            EExposureAuto       = 0x0000,
            /** Night-time setting for long exposures. */
            EExposureNight      = 0x0001,
            /** Backlight setting for bright backgrounds. */
            EExposureBacklight  = 0x0002,
            /** Centered mode for ignoring surroundings. */
            EExposureCenter     = 0x0004,
            /** Sport setting for very short exposures. */
            EExposureSport      = 0x0008,
            /** Generalised setting for very long exposures. */
            EExposureVeryLong   = 0x0010,
            /** Snow setting for daylight exposure. */
            EExposureSnow       = 0x0020,
            /** Beach setting for daylight exposure with reflective glare. */
            EExposureBeach      = 0x0040,
            /** Programmed exposure setting. */
            EExposureProgram    = 0x0080,
            /** Aperture setting is given priority. */
            EExposureAperturePriority   = 0x0100,
            /** Shutter speed setting is given priority. */
            EExposureShutterPriority    = 0x0200,
            /** User selectable exposure value setting. */
            EExposureManual             = 0x0400,
            /** Exposure night setting with colour removed to get rid of colour
                noise. */
            EExposureSuperNight         = 0x0800,
            /** Exposure for infra-red sensor on the camera */
            EExposureInfra              = 0x1000
            };

        /** Specifies how the white balance is set. */
        enum TWhiteBalance
            {
            /** Set white balance automatically. Default, always supported. */
            EWBAuto             = 0x0000,
            /** Normal daylight. */
            EWBDaylight         = 0x0001,
            /** Overcast daylight. */
            EWBCloudy           = 0x0002,
            /** Tungsten filament lighting. */
            EWBTungsten         = 0x0004,
            /** Fluorescent tube lighting */
            EWBFluorescent      = 0x0008,
            /** Flash lighting. */
            EWBFlash            = 0x0010,
            /** High contrast daylight primarily snowy */
            EWBSnow             = 0x0020,
            /** High contrast daylight primarily near the sea */
            EWBBeach            = 0x0040,
            /** User configurable mode */
            EWBManual           = 0x0080,
            /** Shade */
            EWBShade            = 0x0100
            };
        """
        try:
            #camera._my_camera.SetCameraParameters(2, 0, 0, 0, 16, 2, 0)
            #   #aMode, &aSize, &aZoom, &aFlash, &aExp, &aWhite (2, 4, 8, etc),
            #       aISORate

            # aMode, &aSize, &aZoom, &aFlash, &aExp, &aWhite (2, 4, 8, etc),
            #   aISORate
            camera._my_camera.SetCameraParameters(2, 0, 0, 0, 1, 0, 0)

            # camera._my_camera.SetCameraSettings()
            camera._my_camera.ApplyCameraSettings()

            # """
            SleepAndPetWatchdog(10.0)

            # aMode, &aSize, &aZoom, &aFlash, &aExp, &aWhite (2, 4, 8, etc),
            #   aISORate
            #camera._my_camera.SetCameraParameters(2, 0, 0, 0, 16, 2, 1)

            # aMode, &aSize, &aZoom, &aFlash, &aExp, &aWhite (2, 4, 8, etc),
            #   aISORate
            camera._my_camera.SetCameraParameters(2, 0, 0, 0, 16, 0, 0)

            # camera._my_camera.SetCameraSettings()
            camera._my_camera.ApplyCameraSettings()
            # """

            SleepAndPetWatchdog(10.0)
            # aMode, &aSize, &aZoom, &aFlash, &aExp, &aWhite (2, 4, 8, etc),
            #   aISORate
            camera._my_camera.SetCameraParameters(2, 0, 0, 0, 0, 0, 0)
        except:
            DebugPrintErrorTrace()


def StopWatchdog():
    """
    When using the phone manually/directly, the alternatives are:
        - kill the Watchdog and then, when you want to dedicate the phone for iCam,
            restart the phone;
        - make iCam's pause interval much bigger (e.g., 10 hr) and keep the
            watchdog, but then you have to manually come back and redo the
            pause interval.
    """
    if SYMBIAN_OS:
        DebugPrint("Entered StopWatchdog().")

        try:
            """
            WatchDog.exe with ID 0xEF1E172D
            JBakTaskman reports the running process instance with name
                "Watchdog.exe[ef1e172d]0001"
            """
            # From test_kill_process.py:
            #print("found %d processes" % miso.kill_process(u"Y-Brow*", 0))
            #print("found %d processes" % miso.kill_process(u"*[e53540d1]*", 0))

            res = miso.kill_process(u"Watchdog*", 0) # 0 is the reason for kill

            DebugPrint("StopWatchdog(): miso.kill_process(...) returned " \
                        "%d (#processes killed)." % res)
        except:
            DebugPrintErrorTrace()

"""
For Symbian OS:
    http://discussion.forum.nokia.com/forum/showthread.php?125168-Python-for-S60-1.4.2-released
        "Yep. Seems to be in "pys60-1.4.2_src\src\appui\Python_appui.h".
        ### KMaxPythonMenuExtensions 30 ###"
        "Yes, it's an arbitrary limit. The reason it's not unlimited is that an
            entry in the resource file app/Python.rss is needed for every
            submenu entry -- or at least that's the only way we could make it
            work. You'll need to add more entries there if you want to add more
            submenu entries.
        Though, is a menu with over 30 entries really usable? What's the
            use case?"

    http://discussion.forum.nokia.com/forum/showthread.php?144408-appuifw.app.menu-limitations
        "I think it's the number of all list items, including submenus..."
        "Does not generate the error, but the items in the submenu do not
            behave as expected... "

    http://discussion.forum.nokia.com/forum/showthread.php?69342-How-many-levels-for-a-menu-app
        "The question you were originally asking, i.e. depth, is unfortunately
            only 2, meaning that you can have a menu and a submenu in the menu.
            A submenu cannot have submenus - this is limited by the underlying
            UI framework, not the PyS60 implementation."
    PyS60 docs: "The maximum allowed number of items in a menu, or items in a
            submenu, or submenus in a menu is 30."
"""

#!!!!TODO: compute BT_CLIENTS from btMsgMostRecentTime
BT_CLIENTS = [BT_ADDR_6680, BT_ADDR_6120, BT_ADDR_N95]
def ChangePhonesActivity_real(aPauseInterval):
    #if deviceId == IMEI_N82:
    """
    ExecuteCommands("set-pause-interval %d" % aPauseInterval)

    for btAddrClient in BT_CLIENTS:
        ExecuteCommands("send-command-via-bluetooth " + btAddrClient + \
                        " set-pause-interval %d" % (aPauseInterval * 2))
    """

    """
    We create more than one commands file because sending via BT can crash the
        iCam S60 version.
    """
    pathFileName = LOCAL_FOLDER + "/" + COMMANDS_FILENAME

    fOutput = open(pathFileName, "wt")
    fOutput.write("set-pause-interval %d" % aPauseInterval)
    fOutput.close()

    i = 0
    for btAddrClient in BT_CLIENTS:
        i += 1
        fOutput = open(pathFileName + ".%d" % i, "wt")
        fOutput.write("send-command-via-bluetooth " + btAddrClient + \
                        " set-pause-interval %d" % (aPauseInterval * 2))
        fOutput.close()

    DownloadCommands_real()
ChangePhonesActivity = lambda pauseInterval: lambda : ChangePhonesActivity_real(pauseInterval)


def SetMenu(firstTime=False):
    global pauseIntervalStr, photoResolutionStr, phoneModel, deviceId
    global readGPS
    global numCamerasSupported
    global menuTable
    global startButtonPressed

    DebugPrint("Entered SetMenu().")

    if ANDROID_OS:
        """
        menuTable = {
            #"Config": {"label": "Config", "event": DisplayDeviceId,
            #               "eventData": None, "iconName": "ic_menu_edit"},

            "Display Device Id": {"event": DisplayDeviceId,
                                    "eventData": None,
                                    "iconName": "ic_menu_edit"},
            "Exit":              {"event": Quit,
                                    "eventData": None,
                                    "iconName": "ic_delete"}
        }
        """

        def CreateOptionsMenu():
            """
            From https://groups.google.com/group/android-scripting/browse_thread/thread/c9f9c43101a93263
            "The options menu is implicitly created when you add items to it. "
            """

            # myDroid.clearOptionsMenu()
            # for myItemKey in menuTable:
            for myItemKey in sorted(menuTable.keys()):
                """
                If the 2nd element of the tuple-value pointed by myItemKey is
                    None it is normally an item used only in PyS60.
                """
                if menuTable[myItemKey][1] is not None:
                    #myDroid.addOptionsMenuItem(myItemKey, myItemKey,
                    #   menuTable[myItemKey]["eventData"],
                    #   menuTable[myItemKey]["iconName"])

                    """
                    If it not None we print a different icon than standard.
                    See http://developer.android.com/reference/android/R.drawable.html
                        for the various icon name strings.
                    """
                    if menuTable[myItemKey][3] is None:
                        myDroid.addOptionsMenuItem(str(menuTable[myItemKey][0]),
                                            myItemKey, menuTable[myItemKey][2])
                    else:
                        myDroid.addOptionsMenuItem(str(menuTable[myItemKey][0]),
                                            myItemKey, menuTable[myItemKey][2],
                                            menuTable[myItemKey][3])

            """
            for myItemKey, itemVal in tel.iteritems():
                myDroid.addOptionsMenuItem(myItemKey, myItemKey,
                                            itemVal["eventData"],
                                            itemVal["iconName"])
            """

        if firstTime:
            if ANDROID_OS_QPYTHON:
            #if False: #ANDROID_OS_QPYTHON:
                startButtonPressed = True
                DisplayNote("Test on QPython.")
                ReactiveLoopOnlyIfStartButtonNotAlreadyPressed()
            else:
                CreateOptionsMenu()
                """
                from threading import Thread
                uiEventHandlerThread = Thread(target = eventHandler)
                uiEventHandlerThread.start()
                """
                #thread.start_new_thread(EventHandler, ())

                MyThreadStart(EventHandler)
            # (videoFileName, videoPathFileName))
    elif SYMBIAN_S60_OS:
    # elif SYMBIAN_OS:
        try:
            """
            IMPORTANT: myAppMenu has at most 23 menu and submenu (-3 which are
                not added for certain phones) items.
            """
            myAppMenuSettings =   ( \
                        (menuTable["0Servers"][0], menuTable["0Servers"][1]), \
                        (u"Select Access Point", SelectAccessPoint), \

                        (menuTable["1Record_Config"][0],
                            menuTable["1Record_Config"][1]), \

                        (u"Photo Config", PhotoConfigMenu), \
                        (u"Pause Interval", PauseIntervalMenu), \
                        #(u"Motion Detection", AnalysisMenu), \
                        (u"Media Analysis", MediaAnalysisMenu), \

                        (menuTable["Select_BT_Mode"][0],
                            menuTable["Select_BT_Mode"][1]), \

                        (u"Misc", MiscellaneousSettingsMenu) \
                    )

            """
            myAppMenu = [ \
                (u"Display phone ID", DisplayDeviceId), \
                (u"Settings", (myAppMenuSettings))
            ]
            """
            # if gdataModulesImported:
            if True:
                myAppMenu = [ \
                    (u"Select Preset Mode", ( \
                        (u"Main Cam: Video Record",
                            SelectPresetMainVideoRecord), \
                        (u"Main Cam: Take Photo", SelectPresetMainTakePhoto), \
                        (u"Main Cam: Burst Photos",
                            SelectPresetMainBurstPhotos), \
                        (u"Main Cam: Burst Videos",
                            SelectPresetMainBurstVideos) \
                        #(u"Burst Video Main", ConfirmQuit) \
                        ), \
                    ), \
                    (menuTable["01CaptureWhat"][0],
                        menuTable["01CaptureWhat"][1]) \
                    #(u"YouTube/Picasa", SelectServersMenu)
                ]
            else:
                myAppMenu = []

            # We (try) do not show Viewfinder menu item while doing video
            #   recording.
            if videoRecordStartTime == -1:
                if viewFinderStarted:
                    if numCamerasSupported >= 1:
                        myAppMenuVF = ((u"Stop Viewfinder",
                                StopViewFinderForCameraCallable), )
                else:
                    if numCamerasSupported == 1:
                        myAppMenuVF = ((u"Start Main Viewfinder",
                                StartViewFinderForCameraCallable(0,
                                True)), )  # IMPORTANT: the comma is required
                    elif numCamerasSupported == 2:
                        myAppMenuVF = ((u"Start Main Viewfinder",
                                StartViewFinderForCameraCallable(0,
                                True)), (u"Start VGA Viewfinder",
                                StartViewFinderForCameraCallable(1,
                                True)))
                myAppMenu = myAppMenu + [(u"Viewfinder", myAppMenuVF)]

            if deviceId == IMEI_E7:
            #if deviceId == IMEI_N82:
                myAppMenu += [ \
                    #(menuTable["Display_device_ID"][0],
                    #   menuTable["Display_device_ID"][1]), \
                    (menuTable["Settings"][0], (myAppMenuSettings)), \
                    (u"SetCameraParams", SetCameraParametersMenu), \
                    (menuTable["Display_Info"][0],menuTable["Display_Info"][1])
                ]
            elif deviceId == IMEI_N82:
            #elif deviceId == IMEI_E7:
                if pauseInterval < 600:
                    menuStrStandbyActive = u"All phones to sleep"
                    pauseStandbyActive = PAUSE_INTERVAL_POWER_MANAGED # / 2
                else:
                    menuStrStandbyActive = u"All phones to active"
                    pauseStandbyActive = 120

                myAppMenu += [ \
                    #(menuTable["Display_device_ID"][0],
                    #   menuTable["Display_device_ID"][1]), \
                    (menuTable["Settings"][0], (myAppMenuSettings)), \
                    #(u"SetCameraParams", SetCameraParametersMenu), \
                    (u"Stop Watchdog", StopWatchdog),
                    (menuStrStandbyActive, ChangePhonesActivity(pauseStandbyActive)),
                    (menuTable["Display_Info"][0],menuTable["Display_Info"][1])
                ]
            else:
                myAppMenu += [ \
                    #(menuTable["Display_device_ID"][0],
                    #   menuTable["Display_device_ID"][1]), \
                    (menuTable["Settings"][0], (myAppMenuSettings)), \
                    (menuTable["Display_Info"][0],menuTable["Display_Info"][1])
                ]

            """
            if numCamerasSupported == 1:
                myAppMenu += [ \
                    (u"Cameras Used", \
                        ( \
                            (u"%s" % camerasUsedStr[0],  SetCamerasUsed(0)), \
                            (u"%s" % camerasUsedStr[3],  SetCamerasUsed(3))  \
                        ) \
                    )
                ]
            elif numCamerasSupported == 2:
                myAppMenu += [ \
                    (u"Cameras Used", \
                        ( \
                            (u"%s" % camerasUsedStr[0],  SetCamerasUsed(0)), \
                            (u"%s" % camerasUsedStr[1],  SetCamerasUsed(1)), \
                            (u"%s" % camerasUsedStr[2],  SetCamerasUsed(2)), \
                            (u"%s" % camerasUsedStr[3],  SetCamerasUsed(3))  \
                        ) \
                    )
                ]
            """

            myAppMenu = myAppMenu + [ \
                #(u"Send Inbox SMSes", UploadInboxSMSes), \
                (u"Help", Help),
                (u"Exit", ConfirmQuit) \
                #(u"Exit", Quit) \
                #(menuTable["0Exit"][0], menuTable["0Exit"][1])
            ]
            #"""
            #(u"Record Duration", SetRecordDurationMenu(0)), \
            #(u"Upload Media", Quit), \
            #(u"Record Main 7 sec", SetRecordDurationMenu(0)), \
            #(u"Record VGA 7 sec", SetRecordDurationMenu(1)), \
            #(u"Restart Cellphone", Quit), \
            #"""

            if firstTime == -1:
                pass
            elif firstTime != -1:
            #if (firstTime == True) or (firstTime == False):
                if startButtonPressed:
                    #myAppMenu = [(u"Start", ReactiveLoop)] + myAppMenu
                    myAppMenu = [(menuTable["00Stop"][0],
                                 menuTable["00Stop"][1])] + myAppMenu
                else:
                    #myAppMenu = [(u"Start", ReactiveLoop)] + myAppMenu
                    myAppMenu = [(menuTable["00Start"][0],
                                 menuTable["00Start"][1])] + myAppMenu

            appuifw.app.menu = myAppMenu
        except:
            (exceptionType, exceptionValue, exceptionTraceback) = \
                sys.exc_info()

            errorStr = "SetMenu(): (free_ram = %d) returned exception %s." \
                        % (GetFreeRAM(),
                            repr(traceback.format_tb(exceptionTraceback)))

            if MY_DEBUG_UPLOAD_MSG:
                UploadGZippedData(deviceId, errorStr, ICAM_SERVER_NAME,
                                        WEBPAGE_UL_GZIPPED_TEXT, None)

            DebugPrint(errorStr)

            if MY_DEBUG_STDERR:
                sys.stderr.write("    " + errorStr + "\n")
            DebugPrintErrorTrace()

        """
        This has the effect of flushing the eventual pending UI events.
        From [Mobile_Python_2007]: "The e32.ao_yield() at the end of the
            loop makes sure that the system leaves some time to register
            the keyboard events, as drawing in the tight loop consumes lots
            of CPU power and might make the system unresponsive."
        From PyS60 2.0 documentation: Yields to the active scheduler to have
            ready active objects with priority above normal scheduled for
            running. This has the effect of flushing the eventual pending UI
            events. Note that the UI callback code may be run in the context of
            the thread that performs an ao_yield. For information on active
            scheduler, see S60 SDK documentation [4].
        """
        e32.ao_yield()
    elif SYMBIAN_UIQ_OS:
        # When executed the code for S60 it gave LDR-IMPORT error, Reason 2??
        pass
    elif WINDOWS_CE_OS_PYTHONCE:
        pass

    DebugPrint("Exiting SetMenu().")


def GetTextPauseInterval():
    res = ""

    try:
        secs = pauseInterval

        if pauseInterval > 60:
            secs = pauseInterval % 60
            mins = pauseInterval / 60
        else:
            mins = 0

        if pauseInterval > 3600:
            mins = mins % 60
            hrs = pauseInterval / 3600
        else:
            hrs = 0

        if pauseInterval > 24 * 3600:
            hrs = hrs % 24
            days = pauseInterval / (24 * 3600)
        else:
            days = 0

        if days > 0:
            res += "%dd " % days
        if (hrs > 0) or (res != ""):
            res += "%02dh " % hrs
        if (mins > 0) or (res != ""):
            res += "%02dm " % mins
        if (secs >= 0) or (res != ""):
            res += "%02ds" % secs
    except:
        res = "INVALID pause value"
        DebugPrintErrorTrace()

    return res


def ClearScreen():
    global displaySizeMax, displaySizeMin

    if SYMBIAN_OS:
        try:
            # Draw a black rectangle to erase the screen.
            if S60_EDITION[0] >= 3:
                if appuifw.app.orientation == "landscape":
                    canvas.rectangle((0, 0, displaySizeMax - 1,
                            displaySizeMin - 1), fill=0) # , width = 20)
                else:
                    # appuifw.app.orientation == "portrait"
                    canvas.rectangle((0, 0, displaySizeMin - 1,
                            displaySizeMax - 1), fill=0) # , width = 20)
            else:
                canvas.rectangle((0, 0, displaySizeMin - 1,
                            displaySizeMax - 1), fill=0) # , width = 20)
        except:
            DebugPrintErrorTrace()

# """
doNotDisplayRedrawInfo = False


myTextMainCam = ""
myTextVGACam = ""
myTextUpload = ""
myTextAPName = ""
myTextVer = ""
myTextGoogle = ""
def ComputeInfoStrings():
    global myTextMainCam, myTextVGACam, myTextUpload, myTextAPName, myTextVer
    global myTextGoogle

    myTextMainCam = "Main camera: "
    if numCamerasSupported > 0:
        if cameraMode[0] == 0:
            myTextMainCam += "not used"
        elif cameraMode[0] == 1:
            myTextMainCam += "video rec, %d [sec]" % videoRecordDuration[0]
            #myTextMainCam += "video rec"
        elif cameraMode[0] == 2:
            myTextMainCam += "take photos"
        elif cameraMode[0] == 3:
            myTextMainCam += "take video and photos"
    myTextMainCam += "."

    myTextVGACam = "VGA camera: "
    if numCamerasSupported >= 2:
        if cameraMode[1] == 0:
            myTextVGACam += "not used"
        elif cameraMode[1] == 1:
            myTextVGACam += "video rec, %d [sec]" % videoRecordDuration[1]
            #myTextVGACam += "video rec"
        elif cameraMode[1] == 2:
            myTextVGACam += "take photos"
        elif cameraMode[1] == 3:
            myTextVGACam += "take video and photos"
    myTextVGACam += "."

    myTextUpload = u"Upload to: "
    if uploadMediaToYouTube:
        myTextUpload += "YouTube "
    if uploadMediaToPicasa:
        myTextUpload += "Picasa "

    if uploadMediaToYouTube or uploadMediaToPicasa:
        myTextGoogle = u"Google username: " + googleUsername

    if useiCamServer == 1:
        myTextUpload += "iCam(no media)"
    elif useiCamServer == 2:
        myTextUpload += "iCam(all)"
    myTextUpload += "."

    myTextVer = u"iCam ver: %s." % CURRENT_RELEASE_TIME
    myTextAPName = u"  Access point used: %s." % accessPointName


#!!!!TODO: reuse code from DisplayRedrawInfoDisplayExtensiveInfo(displayMode=1, displayAllInfo=True):
def DisplayRedrawInfo(partialForAndroid=False):
    DebugPrint("Entered DisplayRedrawInfo().")

    myTextAudio = u"Microphone rec duration: %d [sec]." % audioRecordDuration
    myTextPause = u"Pause: %s." % GetTextPauseInterval()

    ComputeInfoStrings()

    if SYMBIAN_OS:
        global doNotDisplayRedrawInfo

        if doNotDisplayRedrawInfo == True:
            DebugPrint("DisplayRedrawInfo(): getting out because " \
                        "doNotDisplayRedrawInfo == True.")
            """
            Otherwise it seems it crashes if we do the following operations,
                while in the AP Selection dialog.
            """
            return

        """
        if SYMBIAN_S60_3RD_ED and (not _PyS60_1_9_OR_NEWER):
            return
        """

        if SYMBIAN_3:
            if appuifw.app.orientation == "portrait":
                DebugPrint("DisplayRedrawInfo(): getting out because " \
                            "appuifw.app.orientation == 'portrait'.")
                """
                Otherwise it seems it crashes if we do the following operations
                    (is it because it is printing out of screen??) - see for ex
                Z:\1PhD\ReVival\Logs\NokiaE7\2011_05_07\stdout_2011_05_07_15_37_43.txt.
                """
                return

        DebugPrint("DisplayRedrawInfo(): here 0.")

        try:
            # At line 0 we have the 3G sign and the application title.
            myDelta = 20

            # if startButtonPressed == False:
            if startButtonPressed:
                canvas.text((10, 10 + myDelta), u"iCam is broadcasting.",
                                fill=(0, 255, 0), font="normal")
                #'annotation', 'title', 'legend', 'symbol', 'dense', 'normal'
            else:
                # Note: the visible canvas for normal display on S60 3rd is of
                #    320 x 210 pixels

                #canvas.text((10,  0 + myDelta), u"Welcome to iCam!",
                #    fill=(0, 255, 255))

                #canvas.text((10, 15 + myDelta), u"Configure iCam, then go " \
                #    "to Options and hit Start to run.", fill = (0, 255, 0))

                #canvas.text((10, 15 + myDelta), u"Configure iCam & Start " \
                #    "Broadcasting.", fill = (255, 0, 0))

                if orientationForThisPhoneModel == "portrait":
                    myText = u"Configure & Start"
                else:
                    myText = u"Configure iCam & Start Broadcasting."

                canvas.text((10, 10 + myDelta), myText,
                            fill=(255, 0, 0), font="normal")

            #canvas.text((10, 30 + myDelta), u"Your device ID: %s." % deviceId,
            #    fill = (255, 255, 255))

            canvas.text((10, 30 + myDelta), myTextAPName, fill=(255, 255, 255))

            canvas.text((10, 45 + myDelta), myTextUpload, fill=(255, 255, 255))

            #canvas.text((10,  60 + myDelta), u"  Pause interval: %d [sec]. " \
            #    "Burst detection %d." % (pauseInterval, motionDetectionIsOn),
            #    fill=(255, 255, 255))

            #canvas.text((10, 60 + myDelta), u"Pause: %d [sec]." % pauseInterval,
            #            fill=(255, 255, 255))
            canvas.text((10, 60 + myDelta), myTextPause, fill=(255, 255, 255))

            """
            if (cameraMode[0] != 0) and (cameraMode[1] == 0):
                myText = "Main"
            elif (cameraMode[0] != 0) and (cameraMode[1] != 0):
                myText = "Main & VGA"
            elif (cameraMode[0] == 0) and (cameraMode[1] != 0):
                myText = "VGA"
            elif (cameraMode[0] == 0) and (cameraMode[1] == 0):
                myText = "None"
            canvas.text((10,  75 + myDelta), u"Cameras used: %s." % (myText),
                        fill = (0, 255, 0))
            """
            canvas.text((10, 75 + myDelta), u"Cameras used:", fill=(0, 255, 0))
            canvas.text((10, 90 + myDelta), u"  %s" % myTextMainCam,
                                                        fill=(0, 255, 0))
            canvas.text((10, 105 + myDelta), u"  %s" % myTextVGACam,
                                                        fill=(0, 255, 0))

            #canvas.text((10,  75 + myDelta), u"Rec [sec]: Cameras %d & %d, " \
            #               "Audio %d." % (videoRecordDuration[0],
            #               videoRecordDuration[1], audioRecordDuration),
            #               fill = (255, 255, 255))
            canvas.text((10, 120 + myDelta), myTextAudio, fill=(0, 255, 0))

            canvas.text((10, 135 + myDelta), myTextVer, fill=(255, 255, 255))
        except:
            DebugPrintErrorTrace()

    elif ANDROID_OS:
        # HTML_PATHFILENAME = "/sdcard/iCam/index.html"
        HTML_PATHFILENAME = LOCAL_FOLDER + "/index.html"

        #if not os.path.isfile(HTML_PATHFILENAME):
        try:
            fOutput = open(HTML_PATHFILENAME, "w")

            myText = myTextUpload

            myText += "<br/>"
            myText += myTextPause + "<br/>"

            myText += "Cameras used:<br/>\n"
            if numCamerasSupported > 0:
                myText += "&nbsp;&nbsp;&nbsp;&nbsp;" + myTextMainCam + "<br/>\n"

            myText += myTextAudio + "<br/>\n"
            myText += myTextVer + "<br/>\n"

            myText += myTextGoogle + "<br/>\n"

            # !!!!TODO:  use """ ... """ instead - like this you don't need to specify \n, etc
            fOutput.write("<html>\n" \
                          "<head>\n" \
                          "<title>iCam</title>\n" \
                          '<style type="text/css">\n' \
                          "body {\n" \
                          "    background-color: #000000;\n" \
                          "}\n" \
                          "body,td,th {\n" \
                          "    color: #00FF00;\n" \
                          "}\n" \
                          ".style1 {font-size: xx-large}\n" \
                          "</style></head>\n" \
                          "<body>\n" \
                          #'<span class="style1">Configure iCam & Start Broadcasting.</span><br/>\n' \
                          '<span class="style1">Welcome to iCam!</span><br/>\n' \
                          "<br/>\n" \
                          #'&nbsp;&nbsp;(Press Menu, Start Broadcasting to start iCam)<br/>\n' + \
                          "Configure & Start iCam!<br/><br/>\n" + \
                          str(myText) + \
                          "</body>\n" \
                          "</html>\n"
                    )
            fOutput.close()

            if not partialForAndroid:
                # wait=False - do NOT "block until the user exits the WebView
                myDroid.webViewShow(HTML_PATHFILENAME, False)
        except:
            DebugPrintErrorTrace()

    DebugPrint("Exiting DisplayRedrawInfo().")




#!!!!TODO: reuse code from DisplayRedrawInfo
def DisplayExtensiveInfo(displayMode=1, displayAllInfo=True):
    global doNotDisplayRedrawInfo
    global canvas

    if ANDROID_OS:
        DisplayNote(u"Your device ID (IMEI) is %s.\n iCam version: %s\n" % (deviceId, CURRENT_RELEASE_TIME))
    elif SYMBIAN_OS:
        ComputeInfoStrings()

        if displayMode == 0:
            try:
                # From http://wiki.forum.nokia.com/index.php/Python_on_Symbian/04._Basic_User_Interface#Text_Editor:
                myTextEditor = appuifw.Text()
                appuifw.app.body = myTextEditor

                # See http://pys60.garage.maemo.org/doc/s60/node21.html
                # It has only digit chars - letters are replaced by
                #    "square" char.
                #myTextEditor.font = (u"Nokia Hindi S60", 12, None)
                myTextEditor.font = "dense"

                myTextEditor.add(myTextVer + u"\n" % CURRENT_RELEASE_TIME)
                myTextEditor.add(u"Your device ID (IMEI) is %s." % deviceId)
                myTextEditor.add(u"\n")
                myTextEditor.add(u"Current configuration:")
                myTextEditor.add(u"\n")
                myTextEditor.add(u"  " + myTextAPName)
                #myTextEditor.add(u"\n")

                """
                myText = u"  Upload to: "
                if uploadMediaToYouTube:
                    myText += "YouTube "
                if uploadMediaToPicasa:
                    myText += "Picasa "
                if useiCamServer > 0:
                    myText += "iCam"
                myText += "."
                myTextEditor.add(myText)
                myTextEditor.add(u"")

                myTextEditor.add(u"  Pause: %d [sec]." % (pauseInterval))
                myTextEditor.add(u"")

                myTextEditor.add(u"  Rec [sec]: Cameras %d & %d, " \
                                    "Microphone %d." \
                                    % (videoRecordDuration[0],
                                        videoRecordDuration[1],
                                        audioRecordDuration))

                myTextEditor.add(u"")
                myTextEditor.add(u"  Photo settings: local=%s, uploaded=%s, " \
                                  "quality=%d." \
                                  % (str(localPhotoResolution),
                                  photoResolutionStr[photoResolutionIndex][0],
                                  photoQuality))

                myTextEditor.add(u"")
                myTextEditor.add(u"  localVideoMode = %s." % str(localVideoMode))
                myTextEditor.add(u"")

                if storeLocallyMedia == 0:
                    storeLocallyMediaStr = "no"
                elif storeLocallyMedia == 1:
                    storeLocallyMediaStr = "yes"
                else:
                    storeLocallyMediaStr = ":o"

                myTextEditor.add(u"  Store locally: %s. Local folder=%s, " \
                                    "media files=%s." \
                                    % (storeLocallyMediaStr, LOCAL_FOLDER,
                                        LOCAL_FOLDER_MEDIA_FILES))

                myTextEditor.add(u"")

                if readGPS:
                    myStr = "GPS in use."
                else:
                    myStr = "GPS not in use."
                myTextEditor.add(u"  %s mode. %s" \
                                % (bluetoothModeList[bluetoothMode], myStr))

                myTextEditor.add(u"")

                myTextEditor.add(u"  Free: RAM=%.2f; drives: C=%.2f, D=%.2f, "\
                                "E=%.2f [MB]." \
                                % (float(GetFreeRAM()) / MEGA_BYTE,
                                float(GetFreeDriveSpace("C:")) / MEGA_BYTE,
                                float(GetFreeDriveSpace("D:")) / MEGA_BYTE,
                                float(GetFreeDriveSpace("E:")) / MEGA_BYTE))

                myTextEditor.add(u"")
                myTextEditor.add(u"  GSM: signal=%d[%s], CC=%d,NC=%d,LAC=%d," \
                                    "CId=%d." \
                                    % (signalStrength, signalUnits,
                                        mobileCountryCode, mobileNetworkCode,
                                        locationAreaCode, cellId))

                myTextEditor.add(u"")
                myTextEditor.add(u"  Battery = %d%%, charger status = %d." \
                        % (GetBatteryLevelPercentage(), GetChargerStatus()))

                #myTextEditor.add()
                """
                return
            except:
                DebugPrintErrorTrace()

        # #####################################################################
        if displayMode == 1:
            doNotDisplayRedrawInfo = True

            DebugPrint("Entered DisplayExtensiveInfo().")

            if SYMBIAN_3:
                if appuifw.app.orientation == "portrait":

                    DebugPrint("DisplayExtensiveInfo(): getting out because " \
                                "appuifw.app.orientation == 'portrait'.")

                    """
                    Otherwise it seems it crashes if we do the following
                        operations (is it because it is printing out of
                        screen??) - see for ex
        Z:\1PhD\ReVival\Logs\NokiaE7\2011_05_07\stdout_2011_05_07_15_37_43.txt.
                    """
                    return

            DebugPrint("DisplayExtensiveInfo(): here 0.")

            try:
                if SYMBIAN_3:
                    appuifw.app.body.begin_redraw()
                elif (S60_EDITION[0] >= 3) and \
                        (orientationForThisPhoneModel == "landscape"): # if not S60_EDITION == (3, 0):
                    try:
                        """
                        Yes, even N82 requires it.
                        Note: begin_redraw() and end_redraw() don't seem to
                            exist on PyS60 1.4.5
                        """
                        appuifw.app.body.begin_redraw()
                    except:
                        DebugPrintErrorTrace()

                ClearScreen()

                #Note: the visible canvas for normal display on S60 3rd is of
                #    320 x 210 pixels
                #canvas.text((10,  0), u"Welcome to iCam!", fill=(0, 255, 0))

                #canvas.text((10, 15), u"Configure iCam, then go to Options " \
                #    "and hit Start to run.", fill=(0, 255, 0))

                #canvas.text((10, 15), u"Configure iCam & Start Broadcasting.",
                #                       fill=(0, 255, 0))

                #canvas.text((10, 15),
                #               u"Your device ID (IMEI) is %s." % deviceId,
                #               fill=(255, 255, 255))
                canvas.text((10, 15), u"Device ID:  %s." % deviceId,
                            fill=(0, 255, 255))

                canvas.text((10, 30), u"Current configuration:",
                            fill=(255, 255, 255))

                DebugPrint("DisplayExtensiveInfo(): here 1.")

                canvas.text((10, 45), u"  Access point used: %s."
                            % accessPointName, fill=(255, 255, 255))

                #canvas.text((10,  60), u"  Upload to: YouTube=%d, Picasa=%d," \
                #    " iCam=%d." % (uploadMediaToYouTube, uploadMediaToPicasa,
                #    useiCamServer), fill=(255, 255, 255))
                myText = u"  %s" % myTextUpload
                canvas.text((10, 60), myText, fill=(255, 255, 255))

                DebugPrint("DisplayExtensiveInfo(): here 2.")

                if motionDetectionIsOn == 0:
                    motionDetectionIsOnStr = "no"
                elif motionDetectionIsOn == 1:
                    motionDetectionIsOnStr = "yes"
                else:
                    motionDetectionIsOnStr = ":o"

                #canvas.text((10,  75), u"  Pause interval: %d [sec]. " \
                #    "Burst detection: %d." % (pauseInterval,
                #    motionDetectionIsOn), fill=(255, 255, 255))
                """
                canvas.text((10, 75),
                            u"  Pause interval: %d [sec]. " \
                                "Burst detection: %s. BATTERY_LEVEL_THRESHOLD: %d%%." % \
                                (pauseInterval, motionDetectionIsOnStr,
                                                BATTERY_LEVEL_THRESHOLD),
                            fill=(255, 255, 255))
                """
                """
                canvas.text((10, 75),
                            u"  Pause interval: %d [sec]. " \
                                "Motion detection: %s." % \
                                (pauseInterval, motionDetectionIsOnStr),
                            fill=(255, 255, 255))
                """
                canvas.text((10, 75),
                            u"  Pause interval: %s. " \
                                "Motion detection: %s." % \
                                (GetTextPauseInterval(), \
                                    motionDetectionIsOnStr),
                            fill=(255, 255, 255))

                #canvas.text((10,  75), u"  Pause: %d [sec]." % (pauseInterval),
                #    fill=(255, 255, 255))

                """
                if (cameraMode[0] != 0) and (cameraMode[1] == 0):
                    myText = "Main"
                elif (cameraMode[0] != 0) and (cameraMode[1] != 0):
                    myText = "Main & VGA"
                elif (cameraMode[0] == 0) and (cameraMode[1] != 0):
                    myText = "VGA"
                elif (cameraMode[0] == 0) and (cameraMode[1] == 0):
                    myText = "none"

                canvas.text((10,  90), u"  Cameras used: %s." % (myText),
                                fill=(255, 255, 255))
                """
                myText = ""

                if numCamerasSupported > 0:
                    myText += myTextMainCam
                if numCamerasSupported >= 2:
                    myText += " " + myTextVGACam

                canvas.text((10, 90), u"  Cameras used: %s." % myText,
                                fill=(255, 255, 255))

                DebugPrint("DisplayExtensiveInfo(): here 3.")

                canvas.text((10, 105),
                            u"  Rec [sec]: Cameras %d & %d, Microphone %d."
                             % (videoRecordDuration[0],
                                videoRecordDuration[1], audioRecordDuration),
                                fill=(255, 255, 255))

                """
                Remember that Nokia 6680 has display of 176 (height?) x 208
                    pixels, out of which we subtract ~40 pixels of the height.
                """
                # if SYMBIAN_3:
                if SYMBIAN_3:
                    if displayAllInfo:
                        DebugPrint("DisplayExtensiveInfo(): here 4.")

                        canvas.text((10,120), u"  Photo settings: local=%s, " \
                                   "uploaded=%s, quality=%d."
                                    % (str(localPhotoResolution),
                                    photoResolutionStr[photoResolutionIndex][0],
                                    photoQuality), fill=(255, 255, 255))

                        DebugPrint("DisplayExtensiveInfo(): here 5.")

                        if videoAudioEnabled == 1:
                            muteStr = "off"
                        else:
                            muteStr = "on"

                        canvas.text((10, 135),
                                    u"  localVideoMode = %s; mute %s."
                                    % (str(localVideoMode), muteStr),
                                    fill=(255, 255, 255))

                        DebugPrint("DisplayExtensiveInfo(): here 6.")

                        if storeLocallyMedia == 0:
                            storeLocallyMediaStr = "no"
                        elif storeLocallyMedia == 1:
                            storeLocallyMediaStr = "yes"
                        else:
                            storeLocallyMediaStr = ":o"

                        canvas.text((10, 150), u"  Store locally: %s. " \
                                    "Local folder=%s, media files=%s." \
                                     % (storeLocallyMediaStr, LOCAL_FOLDER,
                                        LOCAL_FOLDER_MEDIA_FILES),
                                        fill=(255, 255, 255))

                        DebugPrint("DisplayExtensiveInfo(): here 7.")

                        if readGPS:
                            myStr = "GPS in use."
                        else:
                            myStr = "GPS not in use."

                        #canvas.text((10, 165), u"  Bluetooth mode = %d." \
                        #    % (bluetoothMode), fill=(255, 255, 255))
                        canvas.text((10, 165), u"  %s (server is %s). %s"
                                        % (bluetoothModeList[bluetoothMode],
                                            bluetoothServerAddress, myStr),
                                            fill=(255, 255, 255))

                        DebugPrint("DisplayExtensiveInfo(): here 8.")
                        """
                        if readGPS:
                            canvas.text((10, 180), u"  GPS in use. ",
                                            fill=(255, 255, 255))
                        else:
                            canvas.text((10, 180), u"  GPS not in use. ",
                                            fill=(255, 255, 255))
                        """
                        DebugPrint("DisplayExtensiveInfo(): here 9.")

                        canvas.text((10, 180), u"  Free: RAM=%.2f; drives: " \
                                     "C=%.2f, D=%.2f, E=%.2f [MB]." \
                                     % (float(GetFreeRAM()) / MEGA_BYTE,
                                    float(GetFreeDriveSpace("C:")) / MEGA_BYTE,
                                    float(GetFreeDriveSpace("D:")) / MEGA_BYTE,
                                    float(GetFreeDriveSpace("E:")) / MEGA_BYTE),
                                    fill=(255, 255, 255))

                        DebugPrint("DisplayExtensiveInfo(): here 10.")

                        # viewFinderSize
                        canvas.text((10, 195), u"  GSM: signal=%d[%s], " \
                                    "CC=%d,NC=%d,LAC=%d,CId=%d."
                                     % (signalStrength, signalUnits,
                                        mobileCountryCode, mobileNetworkCode,
                                        locationAreaCode, cellId),
                                    fill=(255, 255, 255))

                        """
                        myStr = u"  Battery = %d, charger status = %d." \
                                % (GetBatteryLevelPercentage(), GetChargerStatus())

                        DebugPrint("DisplayExtensiveInfo(): here 11 - " \
                                    "myStr = %s" % str(myStr))
                        """

                        #canvas.text((10, 225), u"  Battery=%.1f, " \
                        #    "charger status=%d." % (GetBatteryLevelPercentage(),
                        #    GetChargerStatus()), fill = (255, 255, 255))
                        canvas.text((10, 210),
                                    u"  Battery: %d%%, charger status: %s. " \
                                    "Battery_Level_Threshold: %d%%." % \
                                        (GetBatteryLevelPercentage(), \
                                        GetChargerStatusStr(), \
                                        BATTERY_LEVEL_THRESHOLD), \
                                    fill=(255, 255, 255))

                        DebugPrint("DisplayExtensiveInfo(): here 12.")

                if SYMBIAN_3:
                    appuifw.app.body.end_redraw()
                elif (S60_EDITION[0] >= 3) and \
                        (orientationForThisPhoneModel == "landscape"): # if not S60_EDITION == (3, 0):
                    try:
                        """
                        Yes, even N82 requires it. Note: begin_redraw() and
                            end_redraw() don't seem to exist on PyS60 1.4.5.
                        """
                        appuifw.app.body.end_redraw()
                    except:
                        DebugPrintErrorTrace()

                    """
                    On N82 - see stdout_2011_05_18_10_19_26.txt:
                        Exiting DisplayExtensiveInfo().
                        Entered RedrawHandler(): rect = (0, 0, 320, 198)
                        Entered DisplayRedrawInfo().
                    """
                    # SleepAndPetWatchdog(3.0)

                    """
                    IMPORTANT: Do not use SleepAndPetWatchdog because it can
                        mess up (basically it seems it cancels the timer and it
                        doesn't continue) the already issued
                        SleepAndPetWatchdog() from ReactiveLoop (this function,
                        DisplayExtensiveInfo() is called asynchronously).
                    """
                    # We sleep because we want to force RedrawHandler() to be
                    #    issued, while doNotDisplayRedrawInfo = True.
                    e32.ao_sleep(2.0)

                doNotDisplayRedrawInfo = False

                """
                SOCKET_DEFAULT_TIMEOUT, uploadUnsentData,
                    uploadHowManyOfLatestBluetoothMessages

                battery = %d. charger_status = %d
                signalStrength
                free_ram, drive

                # From GetTextForState(cameraId):
                resText = "Free space in bytes on drive C = %d, " \
                            "on drive E = %d. " % (GetFreeDriveSpace("C:"),
                            GetFreeDriveSpace("E:"))

                resText += "free_ram = %d. GSM network signal " \
                            "strength = %d [%s]. Battery = %d, " \
                            "charger_status = %d. Pause interval " \
                            "(pauseInterval) = %d. " \
                            % (GetFreeRAM(), signalStrength, signalUnits,
                                GetBatteryLevelPercentage(),
                                GetChargerStatus(), pauseInterval)

                resText += "Resolution (photoResolutionIndex) = %d. " \
                            "photoModeIndex = %d. digitalZoom = %d. " \
                            "photoQuality = %d. exposureIndex[0] = %d. " \
                            % (photoResolutionIndex, photoModeIndex[cameraId],
                                digitalZoom, photoQuality, exposureIndex[0])

                resText += "whiteBalanceIndex[0] = %d. exposureIndex[1] = %d."\
                            " whiteBalanceIndex[1] = %d. flashIndex = %d. " \
                            "audioRecordDuration = %d. " \
                            % (whiteBalanceIndex[0], exposureIndex[1],
                                whiteBalanceIndex[1], flashIndex,
                                audioRecordDuration)

                resText += "rotateDegreesImage = %d. mobileCountryCode = %d. "\
                            "mobileNetworkCode = %d. locationAreaCode = %d. " \
                            "cellId = %d." % (rotateDegreesImage,
                            mobileCountryCode, mobileNetworkCode,
                            locationAreaCode, cellId)
                """
            except:
                DebugPrintErrorTrace()

            DebugPrint("Exiting DisplayExtensiveInfo().")


if SYMBIAN_OS:
    counter = 0

    def AccSensorCallback(sensorData):
        global counter
        counter = counter + 1
        if counter % 35 == 0:
            DebugPrint("AccSensor: %s" % str(sensorData))
        """
        global counter, myCanvas
        counter = counter + 1
        if (counter % 15) == 0:
            myCanvas.clear()
            myCanvas.text((10, 175), u"A: %04d %04d %04d" %
                        (sensorData["data_1"], sensorData["data_2"],
                         sensorData["data_3"]),
                        font = "title", fill = 0xff0000)

        DebugPrint("AccSensor: %s" % str(sensorData))
        """


def RotSensorCallback(sensorData):
    #global myCanvas
    #appuifw.Canvas().clear()
    #myCanvas.text((10, 140), u"R: %04d %04d %04d" %
    #    (sensorData["data_1"], sensorData["data_2"], sensorData["data_3"]),
    #    font = "title", fill = 0xff0000)

    #if True:
    DebugPrint("RotSensor: %s" % str(sensorData))


try:
    import zipfile
    zipfileImported = True
except:
    zipfileImported = False
    DebugPrint("Not able to import the zipfile module.")
    DebugPrintErrorTrace()


def CheckZipFile(pathFileName):
    # We assume the file exists.

    try:
        if not zipfile.is_zipfile(pathFileName):
            myText = "CheckZipFile(): %s is NOT a valid zip file." % \
                                                            pathFileName

            DebugPrint(myText)

            if MY_DEBUG_UPLOAD_MSG:
                UploadGZippedData(deviceId, myText, ICAM_SERVER_NAME,
                                  WEBPAGE_UL_GZIPPED_TEXT, None)

            return -1

        """
        DebugPrint("CheckZipFile(): %s is a valid pkzip file." % pathFileName)
        """

        #print '-'*40

        # Open the zipped file.
        myZipFile = zipfile.ZipFile(pathFileName, "r")

        if MY_DEBUG_STDOUT:
            print "CheckZipFile(): The contents of the update ZIP file are:"
            myZipFile.printdir()
            sys.stdout.flush()
        #print '-'*40

        checkFilenameList = ["a.pyc"]

        # Enumerate each archived file and open the manifest.txt.
        for myZipInfo in myZipFile.infolist():
            fileName = myZipInfo.filename

            # checkFilenameList.remove(fileName)
            for indexList in range(len(checkFilenameList)):
                if checkFilenameList[indexList] == fileName:
                    checkFilenameList.pop(indexList)

            if fileName == "manifest.txt":
            # if fname.endswith(".txt"):
                # decompress each file's data
                manifestData = myZipFile.read(fileName)

                DebugPrint( ("The contents of %s are:\n" % fileName) +
                            manifestData )

                # Read manifest.txt to get the update release time and compare
                #   it with the current version release time.
                lineList = manifestData.splitlines()
                # Just in case 
                lineList[0] = lineList[0].rstrip(" \r\n")
                tokens = lineList[0].split(": ")

                updateReleaseTime = tokens[1]
                if updateReleaseTime <= CURRENT_RELEASE_TIME:
                    myText = "CheckZipFile(): The installed version of iCam " \
                            "seems to be the most recent one: " \
                            "updateReleaseTime = %s, " \
                            "CURRENT_RELEASE_TIME = %s." % \
                            (updateReleaseTime, CURRENT_RELEASE_TIME)

                    DebugPrint(myText)

                    if MY_DEBUG_UPLOAD_MSG:
                        UploadGZippedData(deviceId, myText,
                                ICAM_SERVER_NAME,
                                WEBPAGE_UL_GZIPPED_TEXT, None)

                    return -1
                """
                # save the decompressed data to a new file
                filename = 'unzipped_' + fname
                fout = open(filename, "wb")
                fout.write(data)
                fout.close()
                print "New file created --> %s" % filename
                """
        if checkFilenameList != []:
            DebugPrint("CheckZipFile(): The update ZIP file does not " \
                        "contain the following indicated files: %s" % \
                                               str(checkFilenameList))

            return -1

        return 0
    except:
        (exceptionType, exceptionValue, exceptionTraceback) = \
            sys.exc_info()

        myText = "Exception in CheckZipFile(). Details: time = %s, " \
                    "free_ram = %d. %s." % \
                    (GetCurrentDateTimeStringNice(), GetFreeRAM(),
                        repr(traceback.format_tb(exceptionTraceback)))
        """
        if MY_DEBUG_UPLOAD_MSG:
            UploadGZippedData(deviceId, myText, ICAM_SERVER_NAME,
                                WEBPAGE_UL_GZIPPED_TEXT, None)
        """
        DebugPrint(myText)

        #if MY_DEBUG_STDERR:
        #    sys.stderr.write(myText + "\n")
        DebugPrintErrorTrace()

        return -1


def ApplyUpdate(UPDATE_PATH_FILENAME, TARGET_UPDATE_PATH_FILENAME,
                                        TARGET_UPDATE_PATH_FILENAME_BACKUP):
    if (not zipfileImported) or (CheckZipFile(UPDATE_PATH_FILENAME) == 0):
        # We check even more for validity of update Zip file, if we can,
        #    and if it is OK we apply the update.
        try:
            # if os.path.exists(TARGET_UPDATE_PATH_FILENAME_BACKUP):
            if os.path.isfile(TARGET_UPDATE_PATH_FILENAME_BACKUP):
                os.unlink(TARGET_UPDATE_PATH_FILENAME_BACKUP)
        except:
            DebugPrintErrorTrace()

        try:
            MoveFileBetweenAnyDrives(TARGET_UPDATE_PATH_FILENAME,
                                        TARGET_UPDATE_PATH_FILENAME_BACKUP)

            MoveFileBetweenAnyDrives(UPDATE_PATH_FILENAME,
                                        TARGET_UPDATE_PATH_FILENAME)

            # CopyFile("E:/private/e21e55ef/lib.zip",
            #    "E:/private/e21e55ef/lib_copy.zip")
            myText = "Found update %s. Copied it. Restart app to run updated " \
                        "version." % UPDATE_PATH_FILENAME

            DebugPrint(myText)

            if MY_DEBUG_UPLOAD_MSG:
                UploadGZippedData(deviceId, myText, ICAM_SERVER_NAME,
                                  WEBPAGE_UL_GZIPPED_TEXT, None)

            appuifw.note(unicode(myText), "info")
        except:
            DebugPrintErrorTrace()


def AutoUpdate_real():
    """
    os.getcwd() returns the place where lib.zip is
        installed - e.g., "E:\Private\e21e55ef".
    """
    DebugPrint("Entered AutoUpdate(): os.getcwd() = %s." % os.getcwd())

    try:
        PetWatchdog()

        if ANDROID_OS:
            pass
        elif SYMBIAN_S60_OS:
        # elif SYMBIAN_OS:
            """
            We do not support yet update for iCam for PyS60 1.4.x
                (i.e., S60 2nd edition)
                !!!!TODO: copy automatically the a.pyc to the iCam app folder.
            """
            if _PyS60_1_9_OR_NEWER == False:
                return

            # We update iCam for PyS60 1.4.x (S60 3rd+ edition)
            LOCAL_FOLDER_BACKUP = LOCAL_FOLDER + "/Backup"
            if not os.path.exists(LOCAL_FOLDER_BACKUP):
                os.makedirs(LOCAL_FOLDER_BACKUP)

            LOCAL_FOLDER_UPDATE_FROM_INTERNET = LOCAL_FOLDER + "/Update"
            if not os.path.exists(LOCAL_FOLDER_UPDATE_FROM_INTERNET):
                os.makedirs(LOCAL_FOLDER_UPDATE_FROM_INTERNET)
            LOCAL_FOLDER_UPDATE_FROM_INTERNET = LOCAL_FOLDER \
                + "/Update/FromInternet"
            if not os.path.exists(LOCAL_FOLDER_UPDATE_FROM_INTERNET):
                os.makedirs(LOCAL_FOLDER_UPDATE_FROM_INTERNET)

            UPDATE_FILENAME = "lib.zip"
            UPDATE_FROM_INTERNET_PATH_FILENAME = \
                LOCAL_FOLDER_UPDATE_FROM_INTERNET + "/" + \
                UPDATE_FILENAME
            # TARGET_UPDATE_PATH_FILENAME = "E:/private/e21e55ef/lib.zip"
            TARGET_UPDATE_PATH_FILENAME = os.getcwd() + "\\lib.zip"
            TARGET_UPDATE_PATH_FILENAME_BACKUP = \
                TARGET_UPDATE_PATH_FILENAME + STATE_BACKUP_EXTENSION

            # STEP 1: Make a copy of file default.py to LOCAL_FOLDER.
            if S60_EDITION[0] >= 3:
                PYTHON_SCRIPT_PATH_FILENAME = "./default.py"
            else:
                PYTHON_SCRIPT_PATH_FILENAME = "./default.py"

            # CopyFile(PYTHON_SCRIPT_PATH_FILENAME, E:/iCam_default_copy.py")

            # CopyFile(PYTHON_SCRIPT_PATH_FILENAME,
            #   LOCAL_FOLDER + "/iCam_default_copy.py")
            CopyFile(PYTHON_SCRIPT_PATH_FILENAME, LOCAL_FOLDER_BACKUP + \
                        "/iCam_default_copy.py")

            # STEP 2: Make a copy of file lib.zip to LOCAL_FOLDER.
            if S60_EDITION[0] >= 3:
                PYTHON_SCRIPT_PATH_FILENAME = "./lib.zip"
            else:
                PYTHON_SCRIPT_PATH_FILENAME = "./lib.zip"

            # PYTHON_SCRIPT_PATH_FILENAME_COPY = "E:/lib_copy.zip"
            PYTHON_SCRIPT_PATH_FILENAME_COPY = LOCAL_FOLDER_BACKUP + \
                                                "/lib_copy.zip"

            if not os.path.isfile(PYTHON_SCRIPT_PATH_FILENAME_COPY):
                CopyFile(PYTHON_SCRIPT_PATH_FILENAME,
                         PYTHON_SCRIPT_PATH_FILENAME_COPY)

            # STEP 3: Update from Memory card, if there is such update at
            #   UPDATE_FROM_DISK_PATH_FILENAME (see below).
            UPDATE_FROM_DISK_PATH_FILENAME = LOCAL_FOLDER + \
                                        "/Update/FromLocal/" + UPDATE_FILENAME

            if os.path.isfile(UPDATE_FROM_DISK_PATH_FILENAME):
                ApplyUpdate(UPDATE_FROM_DISK_PATH_FILENAME,
                            TARGET_UPDATE_PATH_FILENAME,
                            TARGET_UPDATE_PATH_FILENAME_BACKUP)
                # We tried updating and don't continue any other update.
                return

            # At least for Symbian we check for uppercase of UPDATE_FILENAME,
            #   as well. Note: even if on Win the file on memory card looks
            #   lowercase on Symbian it might be uppercase :)
            UPDATE_FROM_DISK_PATH_FILENAME = LOCAL_FOLDER + \
                            "/Update/FromLocal/" + str.upper(UPDATE_FILENAME)

            if os.path.isfile(UPDATE_FROM_DISK_PATH_FILENAME):
                ApplyUpdate(UPDATE_FROM_DISK_PATH_FILENAME,
                            TARGET_UPDATE_PATH_FILENAME,
                            TARGET_UPDATE_PATH_FILENAME_BACKUP)
                # We tried updating and don't continue any other update.
                return

            # STEP 4: Update from the Internet, if a newer Release version is
            #   available.
            if not accessPointRetryConnect:
                try:
                    # Look for Python runtime for Symbian, Android, etc!!!!
                    updateReleaseTime = urllib.urlopen("http://" +
                                        ICAM_SERVER_NAME + WEB_FOLDER +
                                        "/GetLatestVersion.php").read()

                    if (deviceId == IMEI_N95) and \
                            (updateReleaseTime.find(IMEI_N95) == -1):
                        DebugPrint("AutoUpdate(): phone with deviceId = %s " \
                                    "expects update with updateReleaseTime " \
                                    "containing its deviceId (i.e., built " \
                                    "for it as well). Since this is not the " \
                                    "case - updateReleaseTime = %s - we bail" \
                                    " out." % (deviceId, updateReleaseTime))

                        return

                    #dataUncompressed = dataCompressed.decode("zlib")

                    # We might have updateReleaseTime ==
                    #   "2011_05_07_09_00_00_N95N95N95N95N95" and we want only
                    #   to look at the date.
                    if updateReleaseTime[:len(CURRENT_RELEASE_TIME)] <= \
                                    CURRENT_RELEASE_TIME:
                        myText = "AutoUpdate(): The installed version of " \
                                    "iCam seems to be the most recent one: " \
                                    "updateReleaseTime = %s, " \
                                    "CURRENT_RELEASE_TIME = %s." % \
                                    (updateReleaseTime, CURRENT_RELEASE_TIME)

                        if MY_DEBUG_UPLOAD_MSG:
                            UploadGZippedData(deviceId, myText,
                                ICAM_SERVER_NAME, WEBPAGE_UL_GZIPPED_TEXT, None)

                        DebugPrint(myText)

                        return
                    else:
                        myText = "AutoUpdate(): The installed version of " \
                                    "iCam is out-of-date, so we update: " \
                                    "updateReleaseTime = %s, " \
                                    "CURRENT_RELEASE_TIME = %s." % \
                                    (updateReleaseTime, CURRENT_RELEASE_TIME)

                        if MY_DEBUG_UPLOAD_MSG:
                            UploadGZippedData(deviceId, myText,
                                ICAM_SERVER_NAME, WEBPAGE_UL_GZIPPED_TEXT, None)

                        DebugPrint(myText)

                    dataUpdate = urllib.urlopen("http://" +
                            ICAM_SERVER_NAME + WEB_FOLDER + "/Files/" +
                            UPDATE_FILENAME).read()
                except:
                    dataUpdate = ""
                    (exceptionType, exceptionValue, exceptionTraceback) = \
                                                                sys.exc_info()
                    myText = "Exception in AutoUpdate() when downloading " \
                            "file. Details: time = %s, free_ram = %d. %s." \
                            % (GetCurrentDateTimeStringNice(),
                                GetFreeRAM(),
                                repr(traceback.format_tb(exceptionTraceback)))

                    if MY_DEBUG_UPLOAD_MSG:
                        UploadGZippedData(deviceId, myText,
                            ICAM_SERVER_NAME, WEBPAGE_UL_GZIPPED_TEXT, None)

                    DebugPrint(myText)

                    if MY_DEBUG_STDERR:
                        # traceback.print_exc()
                        sys.stderr.write(myText + "\n")
                        sys.stderr.flush()

                    return

                try:
                    myText = "AutoUpdate(): len(dataUpdate) = %d." \
                                % len(dataUpdate)

                    DebugPrint(myText)

                    if MY_DEBUG_UPLOAD_MSG:
                        UploadGZippedData(deviceId, myText,
                            ICAM_SERVER_NAME, WEBPAGE_UL_GZIPPED_TEXT, None)

                    if (len(dataUpdate) > 2) and (dataUpdate[0] == "P") and \
                                                        (dataUpdate[1] == "K"):

                        # To avoid getting an invalid ZIP file we check only
                        #   the magic number from the header (e.g., an ad from
                        #   110mb.com :) ) - in fact this might not be good
                        #   enough.

                        """
                        if len(dataUpdate) == \
                                os.path.getsize(TARGET_UPDATE_PATH_FILENAME):

                            myText = "AutoUpdate(): The installed version " \
                                    "of iCam seems to be the most recent one."

                            if MY_DEBUG_UPLOAD_MSG:
                                UploadGZippedData(deviceId, myText,
                                    ICAM_SERVER_NAME,
                                    WEBPAGE_UL_GZIPPED_TEXT, None)

                            DebugPrint(myText)

                            return
                        """

                        #fOutput = open(LOCAL_FOLDER + "/FilesFromServer/" +
                        #   fileName, "wb")
                        fOutput = open(UPDATE_FROM_INTERNET_PATH_FILENAME,"wb")
                        fOutput.write(dataUpdate)
                        fOutput.close()

                        ApplyUpdate(UPDATE_FROM_INTERNET_PATH_FILENAME,
                                    TARGET_UPDATE_PATH_FILENAME,
                                    TARGET_UPDATE_PATH_FILENAME_BACKUP)
                    else:
                        myText = "AutoUpdate(): What we downloaded does not " \
                                    "seem to be an update for iCam."

                        DebugPrint(myText)

                        if MY_DEBUG_UPLOAD_MSG:
                            UploadGZippedData(deviceId, myText,
                                    ICAM_SERVER_NAME,
                                    WEBPAGE_UL_GZIPPED_TEXT, None)
                except:
                    DebugPrintErrorTrace()

            """
            EasyUpdate (that is the program autoupdates by copying
                UPDATE_FILENAME if it exists as the new iCam default.py
                script - it is the only way to replace the script, since only
                the program can rewrite its file, otherwise if I want another
                program to do the update I need to have AllFiles capabilities).

            try:
                UPDATE_FILENAME = "E:/iCam.py"
                if os.path.exists(UPDATE_FILENAME):
                    appuifw.note(unicode("Found update. Copying it."), "info")
                    print "Found update. Copying it."
                    sys.stdout.flush()
                    CopyFile(UPDATE_FILENAME, "E:/private/e21e55ef/default.py")
                    os.unlink(UPDATE_FILENAME)
            except:
                exceptionType, exceptionValue, exceptionTraceback = \
                        sys.exc_info()

                appuifw.note(unicode(repr(
                    traceback.format_tb(exceptionTraceback))), "info")

                traceback.print_exc()
                sys.stdout.flush()
                sys.stderr.flush()
            """
        elif WINDOWS_CE_OS_PYTHONCE:
            pass
    except:
        DebugPrintErrorTrace()


def AutoUpdate():
    #if USE_ICAM_SERVER:
    #if useiCamServer > 0:

    """
    AutoUpdate checks on the LocalDrive folder and on the Internet if there
        is a new version of the application (what the update looks like should
        depend on the smartphone OS and its' version.
    """
    AutoUpdate_real()

    """
    We MUST NOT start a new thread for AutoUpdate since:
        - I guess it's not a good idea, in principle
        - in AutoUpdate we make use of brief UI elements which need to run in
            the main thread.
    #BAD IDEA: thread.start_new_thread(AutoUpdate_real, ())
    #BAD IDEA: MyThreadStart(AutoUpdate_real)
    """


def UploadUnsentLogs():
    global LOCAL_FOLDER, MY_DEBUG_STDOUT
    global STDERR_FILENAME_PREFIX, STDOUT_FILENAME_PREFIX
    global stdoutFileName, stderrFileName
    global uploadUnsentData

    hasDownloadedNewCmd = DownloadCommands()


    """
    DebugPrint("Entered UploadUnsentLogs(): uploadUnsentData = %d." % \
                                                        uploadUnsentData)
    """
    DebugPrint("Entered UploadUnsentLogs().")

    """
    if conserveEnergy or ((uploadUnsentData != 2) and (uploadUnsentData != 3)):
        return
    """

    try:
        if not os.path.exists(LOCAL_FOLDER_SENT_LOGS):
            os.makedirs(LOCAL_FOLDER_SENT_LOGS)
    except:
        DebugPrintErrorTrace()

    try:
        pathFolderName = LOCAL_FOLDER
        folderContent = os.listdir(pathFolderName)

        """
        Use reverse = False to send first the oldest ones (like this you send
           in chronological order). Use reverse = True for sending first the
            most recent ones.
        """
        # sortedFolderContent = sorted(folderContent, reverse = False)

        # sort() without parameters is the ONLY one that works in Python 2.2.
        #   (Info on sort at http://wiki.python.org/moin/HowTo/Sorting/.)
        folderContent.sort()
        sortedFolderContent = folderContent

        DebugPrint("UploadUnsentLogs(): sortedFolderContent = %s." % \
                                                        sortedFolderContent)

        for fileName in sortedFolderContent:
            if (fileName.find(STDERR_FILENAME_PREFIX) != -1) or \
                (fileName.find(STDOUT_FILENAME_PREFIX) != -1):

                # Since we can't move currently open files we do not process
                #   them now.
                if fileName == stdoutFileName or fileName == stderrFileName:
                    pass
                else:
                    pathFileName = pathFolderName + "/" + fileName

                    # if os.path.isdir(pathFileName):
                    if os.path.isfile(pathFileName):
                        fileSize = os.path.getsize(pathFileName)

                        if fileSize < 10 * MEGA_BYTE:
                            myText = "UploadUnsentLogs(): sending log file %s "\
                                        "of %d bytes at %s." \
                                        % (fileName, fileSize,
                                           GetCurrentDateTimeStringNice())

                            DebugPrint(myText)

                            if MY_DEBUG_UPLOAD_MSG:
                                UploadGZippedData(deviceId, myText,
                                        ICAM_SERVER_NAME,
                                        WEBPAGE_UL_GZIPPED_TEXT, None)
                            """
                            if UploadFile(pathFileName, ICAM_SERVER_NAME,
                                            WEBPAGE_UL_GZIPPED_FILE) != -1:

                                # We do not move stdout/stderr_Watchdog.txt
                                #   files because we want to keep them together
                                #   for the whole run. But we move
                                #   stdout/stderr_Watchdog_*.txt
                                if (fileName.find("Watchdog.txt") == -1):
                                    MoveFileBetweenAnyDrives(pathFileName,
                                            LOCAL_FOLDER_SENT_LOGS + "/" + \
                                                    fileName)
                            """
                            UploadFile(pathFileName, ICAM_SERVER_NAME,
                                    WEBPAGE_UL_GZIPPED_FILE)

                            """
                               We do not move stdout/stderr_Watchdog.txt files
                            because we want to keep them together for the
                            whole run. But we move stdout/stderr_Watchdog_*.txt
                            """
                            if fileName.find("Watchdog.txt") == -1:
                                MoveFileBetweenAnyDrives(pathFileName,
                                       LOCAL_FOLDER_SENT_LOGS + "/" + fileName)
    except:
        # break
        DebugPrintErrorTrace()


def GeneralUseCameraTest(cameraId):
    def MyVideoRecordCallback(errorCode, eventType):
        pass

    #"""
    # Not using this line --> gave error in camera2.py line 270
    #   saying NoneType
    camera._my_camera = camera._camera.Camera(0)

    # After this command the phone restarts.
    #camera.start_finder(ViewFinderCallback, True, viewFinderSize)
    #"""

    """
    DebugPrint("Before camera._camera.Camera(1)")
    camera._my_camera = camera._camera.Camera(1)

    DebugPrint("After camera._camera.Camera(1)")
    camera.device[1] = camera._my_camera

    # The thread crashes here - it says:
    #    "Application closed: InFrameProcThread."
    pic = camera.take_photo("RGB", (320, 240), 0, "none", "auto",
                    "auto", 1)
    """

    """
    camera.device[0] = camera._my_camera

    # The thread crashes here - it says:
    #    "Application closed: OutFrameProcThread.
    pic = camera.take_photo("RGB", (640, 480), 0, "none", "auto",
                    "auto", 0)
    """

    # Save the photo (as JPEG) with maximum quality (100%) locally.
    #pic.save(r"E:\RGB.jpg", None, None, 100)

    # """
    # After this command the phone restarts when camera is 1.
    camera.start_finder(ViewFinderCallback, True, viewFinderSize)

    DebugPrint("After camera.start_finder()")

    """
    # Seems not to work:
    #camera.start_record(r"E:\my_vid_2.mp4", MyVideoRecordCallback,
    #   format = "EFormatYUV420Planar", frameSize = (320, 240),
    #   frameRate = 0.0,
    #   videoType = "video/mp4v-es; profile-level-id=4",
    #   audioEnabled = True)

    #camera.start_record(r"E:\my_vid_2.mp4", MyVideoRecordCallback,
    #   format = "EFormatYUV420Planar", frameSize = (176, 144),
    #   frameRate = 30.0, videoType = "", audioEnabled = True)
    """
    # No video with camera2.
    camera.start_record(r"E:\my_vid_camera.mp4", MyVideoRecordCallback)

    e32.ao_sleep(5.0)
    camera.stop_record()

    # """
    # if SYMBIAN_OS:
    if SYMBIAN_S60_OS:
        # The application waits for the signal from Quit(), in
        #    order to exit.
        appLock.wait()


def GeneralUseCameraG810():
    # Not using this line --> gave error in camera2.py line 270
    #   saying NoneType.
    camera._my_camera = camera._camera.Camera(0)
    camera.device[0] = camera._my_camera

    DebugPrint("After opening camera on SamsungSGH-G810.")
    #DebugPrint("Before camera.start_finder()")

    def MyVideoRecordCallback(errorCode, eventType):
        pass

    # After this command the phone restarts when camera is 1.
    camera.start_finder(ViewFinderCallback, True, viewFinderSize)

    """
    # Seems not to work at all:
    #camera.start_record(r"E:\my_vid_2_new.mp4",
    #   MyVideoRecordCallback, format = "EFormatYUV420Planar",
    #   frameSize = (320, 240), frameRate = 0.0,
    #   videoType = "video/mp4v-es; profile-level-id=4",
    #   audioEnabled = True)

    # Seems not to work:
    #camera.start_record(r"E:\my_vid_EFormatYUV422.mp4",
    #   MyVideoRecordCallback, format = "EFormatYUV422",
    #   frameSize = (320, 240), frameRate = 15.0,
    #   videoType = "video/mp4v-es; profile-level-id=4",
    #   audioEnabled = True)


    # Testing without success video record on G810:
    # Seems not to work:
    #camera.start_record(r"E:\my_vid_3.mp4", MyVideoRecordCallback,
    #   format = "EFormatYUV420Planar", frameSize = (320, 240),
    #   frameRate = 30.0,
    #   videoType = "video/mp4v-es; profile-level-id=4",
    #   audioEnabled = True)
    """
    camera.start_record(
            r"E:\my_vid_3.mp4",
            MyVideoRecordCallback,
            format="EFormatYUV420Planar",
            frameSize=(320, 240),
            frameRate=0.0,
            videoType="video/mp4v-es; profile-level-id=4",
            audioEnabled=True
        )

    e32.ao_sleep(7.0)
    camera.stop_record()

    DebugPrint("After camera.stop_record().")
    #DebugPrint("Before camera.start_finder()")


def GeneralUseCameraS60(cameraId):
    if SYMBIAN_OS:
        try:
            if False:
                GeneralUseCameraTest(cameraId)

            # Samsung G810 specific: !!!!
            if phoneModel == "SamsungSGH-G810":
                GeneralUseCameraG810()
            else:
                if camera2IsImported:
                    # camera.release()
                    camera.UseCamera(cameraId)
                else:
                    """
                    When using camera2, this doesn't do anything - on
                       camera2 we have to explicitely choose a camera.
                    """
                    
                    try:
                        camera.release()
                    except:
                        DebugPrintErrorTrace()

                    camera._my_camera = camera._camera.Camera(cameraId)
        except:
            DebugPrintErrorTrace()


canvas = None
redrawHandlerRunning = None
myImg = None
appLock = None


def RedrawHandler(rect):
    global canvas, myImg
    global stateLoaded
    global redrawHandlerRunning
    global doNotDisplayRedrawInfo

    """
    When inside RedrawHandler() (actually DisplayRedrawInfo()) another
        instance of RedrawHandler() is called which probably messes some
        things up and results in crashing iCam.
    """
    if redrawHandlerRunning == True:
        return

    redrawHandlerRunning = True

    DebugPrint("Entered RedrawHandler(): rect = %s" % str(rect))

    # It probably crashes iCam - indirect recursive call - begin_redraw()
    #    triggers execution of HandleRedraw()
    #canvas.begin_redraw()
    #canvas.end_redraw()
    try:
        if myImg:
            canvas.blit(myImg)

        if doNotDisplayRedrawInfo == True:
            DebugPrint("RedrawHandler(): not calling ClearScreen() " \
                        "because doNotDisplayRedrawInfo == True.")
            """
            !!!!
            return
            """
        else:
            ClearScreen()
    except:
        DebugPrintErrorTrace()

    if stateLoaded:
        DisplayRedrawInfo()
    redrawHandlerRunning = False


def ResizeHandler(newSize):
    DebugPrint("Entered ResizeHandler(): newSize = %s" % str(newSize))


#displaySize
displaySizeMin = -1
displaySizeMax = -1
def SetupSymbian():
    #global keyboard
    global canvas, redrawHandlerRunning, myImg, appLock
    #global displaySize
    global displaySizeMin, displaySizeMax
    global bluetoothSelfAddress, bluetoothSelfName

    DebugPrint("Entered SetupSymbian()\n" + \
            "S60_EDITION=%s, SYMBIAN_OS=%d, SYMBIAN_S60_OS=%d, " \
            "SYMBIAN_S60_2ND_ED=%d, SYMBIAN_S60_3RD_ED=%d, SYMBIAN_1=%d, " \
            "SYMBIAN_3=%d, SYMBIAN_UIQ_OS=%d" % (str(S60_EDITION), SYMBIAN_OS, \
                SYMBIAN_S60_OS, SYMBIAN_S60_2ND_ED, SYMBIAN_S60_3RD_ED, \
                SYMBIAN_1, SYMBIAN_3, SYMBIAN_UIQ_OS))

    try:
        keyboard = Keyboard()

        DebugPrint("SetupSymbian(): keyboard = %s" % str(keyboard))

        # appuifw.app.orientation = "landscape"
        # appuifw.app.menu = None - gives error
        """
        if SYMBIAN_S60_3RD_ED and (not _PyS60_1_9_OR_NEWER):
            pass
        else:
            appuifw.app.title = ICAM_APP_TITLE
        """
        appuifw.app.title = ICAM_APP_TITLE


        """
        appuifw.app.body - From PyS60 2.0 manual:
            "The UI control that is visible in the application's main window.
            Currently either Text, a Listbox object, Canvas, or None".
        """

        """
        t = appuifw.Listbox()
        appuifw.app.body = t
        """

        """
        # From http://croozeus.com/blogs/?p=215:
        #"Text is a text editor UI control that allows text with various formatting
        #   to be displayed."
        #t = appuifw.Text()

        # Set the color of the text:
        t.color = 0xFF0000

        # Set the font by name, size and flags:
        #t.font = (u"Nokia Hindi S60", 25, None)

        # Set the color in which the text will be highlighted:
        #t.highlight_color = 0xFFFF00

        # Highlight the text in a normal way and set the style of the text to
        #    underlined:
        #t.style = (appuifw.HIGHLIGHT_STANDARD | appuifw.STYLE_UNDERLINE)

        # Write text to see the effect:
        t.add(u"This is an example")
        """

        """
        while True:
            e32.ao_sleep(1.0)
        """

        if SYMBIAN_3:
            """
            For Symbian^3 devices we disable the directional_pad - it seems it is
                not supported, and it will MAKE THE CANVAS SMALLER WITHOUT
                DISPLAYING ANYTHING if we enable it.

            From http://wiki.forum.nokia.com/index.php/Python_on_Symbian/07._Graphics_and_Multimedia#Displaying_and_Handling_Image_Objects:
                For these, you can use the canvas to display a virtual directional
                    pad (if the application is in full screen mode, the directional
                    pad is accompanied by two virtual softkeys, as shown in
                    Figure 7.1).

                The pad can be enabled or disabled by setting
                    appuifw.app.directional_pad to True or False, respectively.

            From http://discussion.forum.nokia.com/forum/showthread.php?203133-How-to-get-rid-of-virtual-buttons-and-softkey-labels-on-X6 :
                "Have you tried setting appuifw.app.directional_pad = False ?
                Do that after setting the screen to 'full' ('full_max' is no longer
                used in more recent versions of PyS60, I think it was only
                available in 1.9.4) and before setting the app's body to Canvas and
                it should work."
            """
            appuifw.app.directional_pad = False

        """
        To change the softkey labels see:
            http://discussion.forum.nokia.com/forum/showthread.php?166614-English-softkey-labels
            http://discussion.forum.nokia.com/forum/showthread.php?78957-announce-uikludges-enable-right-sofkey-label-modification
            http://discussion.forum.nokia.com/forum/showthread.php?89557-Controlling-quot-menu-quot-and-quot-exit-quot-labels-in-the-UI
            http://wiki.forum.nokia.com/index.php/Softkey_labels
        """


        redrawHandlerRunning = False

        # if SYMBIAN_OS:
        if SYMBIAN_S60_OS:
            displaySize = sysinfo.display_pixels()
        else:
            displaySize = (100, 100)

        DebugPrint("SetupSymbian(): displaySize = %s." % (str(displaySize)))

        displaySizeMin = displaySize[0]
        displaySizeMax = displaySize[1]
        if displaySizeMax < displaySizeMin:
            displaySizeMin = displaySize[1]
            displaySizeMax = displaySize[0]

        # if SYMBIAN_OS:
        if SYMBIAN_S60_OS:
            """
            myImg = graphics.Image.new(displaySize)
            myImg.rectangle((0, 0, displaySizeMax - 1, displaySizeMax - 1), fill=0)
            #       , width = 20)
            """
            # myImg = graphics.Image.open(r"E:\private\e21e55ef\wallpaper.jpg")
            canvas = appuifw.Canvas(RedrawHandler, keyboard.HandleEvent,
                                    ResizeHandler)
            # """

            # Create an image the size of the screen
            # appuifw.app.body.begin_redraw() # AttributeError: begin_redraw()

            # myImg = graphics.Image.new(sysinfo.display_pixels())
            """
            myImg = graphics.Image.new((360, 640))
            myImg.rectangle((0, 0, 359, 639), fill=0x00FF00, width=20)
            """
            # appuifw.app.body.end_redraw()

            # Make the background a canvas; needed for key capturing
            #                Canvas([redraw_callback=None, event_callback=None,
            #                       resize_callback=None ])
            # canvas = appuifw.Canvas(                 None, keyboard.HandleEvent)
            appuifw.app.body = canvas

            """
            # Test if application is in foreground or background - from
            #    http://discussion.forum.nokia.com/forum/showthread.php?214083-appswitch-module.
            import appuifw
            def cb(fg):
                if(fg):
                    print "foreground"
                else:
                    print "background"

            appuifw.app.focus=cb
            """

            """
            try:
                # 'Canvas' object has no attribute 'resize' .
                canvas.resize((360, 640))
            except:
                DebugPrintErrorTrace()
            """

            appLock = e32.Ao_lock()
            # myTimer = e32.Ao_timer()

            DebugPrint("canvas.size = %s" % str(canvas.size))
            """
            DebugPrint("Before appuifw.app.exit_key_handler: " \
                        "canvas.size = %s" % str(canvas.size))
            """

            appuifw.app.exit_key_handler = ConfirmQuit # Quit

            """
            N82, N95, etc
                240 x 320 pixels
            E7, N8
                360 x 640 pixels
            canvas.size = (360, 480)
            """

            """
            DebugPrint("Before calling appuifw.app.screen: " \
                        "canvas.size = %s" % str(canvas.size))
            """

            # Normal screen with title pane and softkey labels
            appuifw.app.screen = "normal"

            # Only softkey labels visible.
            # appuifw.app.screen = "large"

            # It uses for Canvas the entire screen - no menu appears.
            #    (full screen mode on all devices).
            # appuifw.app.screen = "full"

            """
            DebugPrint("canvas.size = %s\n" % str(canvas.size) + \
                "sysinfo.display_pixels() = %s" % str(sysinfo.display_pixels()))
            """

            """
            canvas.size = (360, 640) # AttributeError: can't set attribute

            DebugPrint("canvas.size = %s" % str(canvas.size))
            """

            """
            # From http://wiki.forum.nokia.com/index.php/Python_on_Symbian/08._Touch_User_Interface#Detecting_a_touch_event_within_a_rectangle:
            def BlueDown(event):
                ''' Blue DOWN event handler '''
                appuifw.note(u"Blue Down")

            import key_codes
            appuifw.app.body.begin_redraw()
            canvas.bind(key_codes.EButton1Down, BlueDown, ((0, 0), (359, 639)))
            appuifw.app.body.end_redraw()
            """

            """
            appuifw.app.body.begin_redraw()
            canvas.rectangle(((0, 0), (359, 639)), fill=(0, 0, 255), width=5)
            appuifw.app.body.end_redraw()
            """
            #canvas.text((100, 100), u"DOWN", fill=(0, 0, 0),
            #        font=(u'Nokia Hindi S60', 40, appuifw.STYLE_BOLD))
    except:
        DebugPrintErrorTrace()

    if misoIsImported:
        try:
            bluetoothSelfAddress = miso.local_bt_address()
        except:
            DebugPrint("MainSetUI(): miso.local_bt_address() returned exception.")

            DebugPrintErrorTrace()

        try:
            """
            It gives a "KErrHardwareNotAvailable" exception if the
                Bluetooth is not on (or maybe even when it is not set
                up - no name and probably never activated).
            """
            bluetoothSelfName = miso.local_bt_name()
        except:
            DebugPrint("MainSetUI(): miso.local_bt_name() returned exception.")
            DebugPrintErrorTrace()

    DebugPrint("Exiting SetupSymbian().")


#!!!!TODO: Put code in LoadMDExtensionScript() in global space - see comments below!!!!
def LoadMDExtensionScript():
    """
    The execfile() has to be issued in the "global space" (i.e., outside functions),
        otherwise the function and variable redefinition might not work,
        probably because of "execfile() cannot be used reliably to modify a
        function's locals" (see below).
    From http://docs.python.org/library/functions.html#execfile.
    Note:
        The default locals act as described for function locals() below:
            modifications to the default locals dictionary should not be attempted.
        Pass an explicit locals dictionary if you need to see effects of the code
            on locals after function execfile() returns.
        execfile() cannot be used reliably to modify a function's locals.
    """
    EXTENSION_MOTION_DETECTION_SCRIPT_FILENAME = "iCam_MD.py"
    EXTENSION_MOTION_DETECTION_SCRIPT_PATH_FILENAME = LOCAL_FOLDER + "/" + \
                                    EXTENSION_MOTION_DETECTION_SCRIPT_FILENAME

    try:
        # if os.path.exists(EXTENSION_MOTION_DETECTION_SCRIPT_PATH_FILENAME):
        if os.path.isfile(EXTENSION_MOTION_DETECTION_SCRIPT_PATH_FILENAME):
            DebugPrint("LoadMDExtensionScript(): Found %s. Loading it." % \
                        EXTENSION_MOTION_DETECTION_SCRIPT_PATH_FILENAME)

            execfile(EXTENSION_MOTION_DETECTION_SCRIPT_PATH_FILENAME)

            DebugPrint("LoadMDExtensionScript(): " \
                        "numHotspots = %d." % numHotspots)
    except:
        DebugPrint("LoadMDExtensionScript(): execfile(%s) returned " \
                    "exception." % EXTENSION_MOTION_DETECTION_SCRIPT_FILENAME)

        DebugPrintErrorTrace()




# For PyObjC
testBedViewControllerInstance = None
NSTimer = None
UIApplicationInstance = None
UIImagePickerControllerInstance = None
UIImagePickerControllerSourceTypeCamera = None
OSName = None



def MainSetUI():
    global orientationForThisPhoneModel

    if ANDROID_OS:
        try:
            DisplayRedrawInfo()
        except:
            """
            DebugPrint("MainSetUI(): Could not check existence/create %s." % \
                                                            HTML_PATHFILENAME)
            """
            DebugPrintErrorTrace()

        if False:
            TestPhoneCall()

        orientationForThisPhoneModel = "portrait"
        SetMenu(True)

    elif SYMBIAN_S60_OS:
    # elif SYMBIAN_OS:
        # audio.say(u"I can't get no satisfaction")

        try:
            if phoneModel in ["Nokia6120", "Nokia5800", "Nokia5230", "Nokia5530", \
                        "NokiaC5", \
                        "NokiaN95", "NokiaN97mini", "NokiaN82", \
                        "NokiaN8", "NokiaE7", "NokiaC5-03", "NokiaC6", "NokiaC7", \
                        "NokiaX6"]:
                orientationForThisPhoneModel = "landscape"
            elif phoneModel in ["Nokia6680", \
                        "NokiaN70", \
                        "NokiaE50-1", "NokiaE63", "NokiaE65", \
                        "NokiaE5", "NokiaE72", "NokiaE75"]:
                orientationForThisPhoneModel = "portrait"
            elif S60_EDITION[0] >= 3:
            # In principle most other 3rd edition phones should be landscape
                orientationForThisPhoneModel = "landscape"
            else:
                orientationForThisPhoneModel = "portrait"

            # We change orientation if necessary to be able to access the biggest
            #   resolutions of the main camera.
            SetUIOrientation(0, False)

            #appuifw.note(u"Welcome to iCam! (Returned from SetUIOrientation() " \
            #                ":) ).", "info")

            appuifw.note(u"Welcome to iCam!", "info")

            """
            try:
                appuifw.app.body.begin_redraw()
                canvas.text((100, 100), u"Welcome to iCam: state!",
                                        fill = (255, 0, 255))
                appuifw.app.body.end_redraw()
            except:
                DebugPrintErrorTrace()
            """

            # To get the right values for cameraPhotoSizes* we need to be in
            #   landscape mode.
            #e32.reset_inactivity()
            #e32.ao_sleep(3)
            #e32.ao_sleep(5)
        except:
            DebugPrintErrorTrace()
    elif SYMBIAN_UIQ_OS:
        orientationForThisPhoneModel = "portrait"
        SetMenu(True)
    elif iOS_PYOBJC:
        orientationForThisPhoneModel = "portrait"
        SetMenu(True)
    elif WINDOWS_CE_OS_PYTHONCE:
        orientationForThisPhoneModel = "portrait"
        SetMenu(True)


# We send this text msg to the iCam server a bit later.
myTextMainCamera2Video = ""

def SetCameraParamsS60():
    global myTextMainCamera2Video
    global numCamerasSupported
    global cameraPhotoSizes_JPEG_Exif, cameraPhotoSizes_RGB24
    global cameraVideoFormats, cameraVideoFrameSizes, cameraVideoModes
    global localVideoMode

    DebugPrint("Entered SetCameraParamsS60().")

    """
    Required to acquire properly the supported camera resolutions, in case
        I have done import camera before changing the app orientation.

    Otherwise, for landscape mode phones we:
        - will not get the biggest resolutions of the main camera
        - the biggest take_photo(camera = 0) might raise expcetion
            ValueError: "Size not supported for camera".

    (This is so because the initalization with supported camera resolutions
        is partly done only once at the very beginning of the camera
        module, which assumes that app orientation cannot change.

    Note, however, that the best solution would be to add in camera.py a
        function that does all these operations.
    """

    """
    # When using camera2, this doesn't do anything - on camera2 we have to
    #   explicitely choose a camera.
    camera.release()
    #camera._my_camera = camera._camera.Camera(0)
    camera.UseCamera(0)
    """
    GeneralUseCameraS60(0)

    """
    camera.device = []
    for dev_index in range(camera.number_of_devices):
        # Used only for image size checking.
        camera.device.append(camera._camera.Camera(dev_index))
    """

    """
    if phoneModel in ["Nokia6120", "NokiaN95", "NokiaN82"]:
        # For phones that take Main camera photos with the Camera App in
        #   landscape mode - Switch to landscape mode, before import
        #   camera, in order to be able to take_photo() at the highest
        #   resolution for the Main camera.
        appuifw.app.orientation = "landscape"
    """

    """
    #import audio
    #audio.Sound.set_volume(0)
    SetUIOrientation(0, True)
    #!!!!I don't know why do I need it.
    #e32.ao_sleep(2) #it was 4 before
    """

    try:
        numCamerasSupported = camera.cameras_available()
    except:
        DebugPrintErrorTrace()

    if phoneModel == "SamsungSGH-G810":
        # The VGA camera doesn't really seem to work at all - it makes the
        #   phone restart when giving take_photo?!!!!
        numCamerasSupported = 1

    # numCamerasSupported = 1
    if numCamerasSupported >= 1:
        """
        camera.release()
        #camera._my_camera = camera._camera.Camera(0)
        camera.UseCamera(0)
        """

        # Retrieving the supported resolutions for the Main camera:
        if orientationForThisPhoneModel == "landscape":
            cameraPhotoSizes_JPEG_Exif[0] = camera.image_sizes("JPEG_Exif")
            #print "cameraPhotoSizes_JPEG_Exif = ", cameraPhotoSizes_JPEG_Exif
            cameraPhotoSizes_RGB24[0] = camera.image_sizes("RGB")
        else:
            #print "cameraPhotoSizes_RGB24 = ", cameraPhotoSizes_RGB24
            #cameraPhotoSizes = [(2592, 1944)] + cameraPhotoSizes

            #cameraPhotoSizes_landscape = \
            #   cameraPhotoSizes_landscape_JPEG_Exif + \
            #   cameraPhotoSizes_landscape_RGB24

            cameraPhotoSizes_JPEG_Exif[0] = camera.image_sizes("JPEG_Exif")
            cameraPhotoSizes_RGB24[0] = camera.image_sizes("RGB")

        """
        if MY_DEBUG_STDOUT:
            print "camera.image_sizes(RGB16) = %s" % \
                    str(camera.image_sizes("RGB16"))
            print "camera.image_sizes(RGB12) = %s" % \
                    str(camera.image_sizes("RGB12"))

            print "camera.image_sizes(JPEG_JFIF) = %s" % \
                    str(camera.image_sizes("JPEG_JFIF"))

            sys.stdout.flush()
        """

        #cameraPhotoSizes_JPEG_Exif = cameraPhotoSizes_JPEG_Exif + [(0, 0)]

    if numCamerasSupported == 2:
        try:
            # Retrieving the supported resolutions for the VGA camera:

            """
            camera.release()
            #camera._my_camera = camera._camera.Camera(1)
            camera.UseCamera(1)
            """
            GeneralUseCameraS60(1)

            cameraPhotoSizes_JPEG_Exif[1] = camera.image_sizes("JPEG_Exif")
            cameraPhotoSizes_RGB24[1] = camera.image_sizes("RGB")

            if camera2IsImported:
                # camera._camera.SetCameraParameters(aMode, aSize, aZoom,
                #   aFlash, aExp, aWhite)

                # camera._camera.SetCameraSettings()
                DebugPrint(
                    "camera._my_video.GetVideoRecordFormatsCount() = %d.\n" % \
                        camera._my_video.GetVideoRecordFormatsCount() +
                    "camera._my_video.GetVideoControllersCount() = %d." % \
                        camera._my_video.GetVideoControllersCount())

                for cId in range(1, -1, -1):
                    cameraVideoFormats[cId] = \
                        camera.GetVideoFrameFormatsSupportedStringList()

                    cameraVideoFrameSizes[cId] = \
                        camera.GetVideoFrameSizes("EFormatYUV420Planar")

                    cameraVideoModes[cId] = \
                        camera.GetVideoFrameRates("EFormatYUV420Planar")

                    localVideoMode[cId] = \
                        (cameraVideoModes[cId][localVideoModeIndex[cId]]["size"],
                         cameraVideoModes[cId][localVideoModeIndex[cId]]["rate"])

                    # cameraVideoModes[1] = ...
                    myText = "The %s camera:\n" % cameraStr[cId]

                    myText += "    Video formats supported: " \
                                "cameraVideoFormats[%d] = %s.\n" % \
                                (cId, str(cameraVideoFormats[cId]))

                    myText += "    TCameraInfo.iVideoFrameFormatsSupported " \
                                "= 0x%X.\n" % \
                                camera._my_camera.video_formats()

                    myText += "    Video frame sizes supported for mode " \
                                "EFormatYUV420Planar: " \
                                "cameraVideoFrameSizes[%d] = %s.\n" % \
                                (cId, str(cameraVideoFrameSizes[cId]))

                    #myText += "    Video frame rates supported: %s.\n" % \
                    #                                    cameraVideoModes[1]

                    myText += "    Video frame rates supported for mode " \
                                "EFormatYUV420Planar: " \
                                "cameraVideoModes[%d] = %s.\n" % \
                                (cId, str(cameraVideoModes[cId]))

                    if cId == 0:
                        myText += "    Video frame sizes supported for mode " \
                                    "EFormatYUV422: %s.\n" % \
                                str(camera.GetVideoFrameSizes("EFormatYUV422"))

                        myText += "    Video frame rates supported for mode " \
                                    "EFormatYUV422: %s.\n" % \
                                str(camera.GetVideoFrameRates("EFormatYUV422"))

                        myText += "    Video frame sizes supported for mode " \
                                    "EFormatEncodedH264: %s.\n" % \
                                str(camera.GetVideoFrameSizes("EFormatEncodedH264"))

                        myText += "    Video frame rates supported for mode " \
                                    "EFormatEncodedH264: %s.\n" % \
                                str(camera.GetVideoFrameRates("EFormatEncodedH264"))

                    """
                    N82: Video frame rates supported
                    [{'size_index': 0, 'rate': 30.0, 'size': (640, 480)},
                    {'size_index': 0, 'rate': 15.0, 'size': (640, 480)},
                    {'size_index': 1, 'rate': 30.0, 'size': (320, 240)},
                    {'size_index': 1, 'rate': 15.0, 'size': (320, 240)},
                    {'size_index': 2, 'rate': 30.0, 'size': (176, 144)},
                    {'size_index': 2, 'rate': 15.0, 'size': (176, 144)}].
                    """
                    for videoMode in cameraVideoFormats[cId]:
                        """
                        for i in range(camera._my_camera.max_frame_size()):
                           fs = camera._my_camera.frame_size(
                                                camera.formatMap[videoMode], i)
                        """
                        for i in range(camera._my_camera.
                                            GetNumVideoFrameSizesSupported()):

                            frameSize = camera._my_camera.frame_size(
                                            camera.formatMap[videoMode], i)

                            if frameSize != (0, 0):
                                myText += "        videoMode %s, index %d, " \
                                            "resolution %4d x %4d\n" % \
                                            (videoMode, i, frameSize[0],
                                                frameSize[1])

                                # ~ for f in range(camera._my_camera.max_frame_rate()):
                                # ~   r=camera._my_camera.frame_rate(camera.
                                #           formatMap[videoMode], f, i,
                                #           camera.exposuremap['auto'])
                                # ~   log("    rate: index %d, value %5.2f" % (f, r))
                    DebugPrint(myText)

                    if cId == 1:
                        #!!!!
                        #camera.release()
                        ##camera._my_camera = camera._camera.Camera(0)
                        #camera.UseCamera(0)
                        GeneralUseCameraS60(0)
                    elif cId == 0:
                        # We send this text msg to the iCam server a bit later, so we save it.
                        myTextMainCamera2Video = myText
        except:
            DebugPrintErrorTrace()

    # Every (Symbian) phone should support this resolution.
    #localVideoMode = [((176, 144), 15.0), ((176, 144), 15.0)]

    DebugPrint(
        "cameraPhotoSizes_JPEG_Exif[0] = %s\n" % \
            str(cameraPhotoSizes_JPEG_Exif[0]) + \
        "cameraPhotoSizes_RGB24[0] = %s" % str(cameraPhotoSizes_RGB24[0]))

    if numCamerasSupported == 2:
        DebugPrint("cameraPhotoSizes_JPEG_Exif[1] = %s\n" % \
                        str(cameraPhotoSizes_JPEG_Exif[1]) + \
                    "cameraPhotoSizes_RGB24[1] = %s" % \
                        str(cameraPhotoSizes_RGB24[1]))

    # In case it returns the wrong resolutions for "portrait" mode
    #   instead of "landscape" mode.
    if phoneModel in ["NokiaN95", "NokiaN82", "NokiaX6", "NokiaE5",
                                                    "NokiaE72"] and \
                        cameraPhotoSizes_JPEG_Exif[0][0] != (2592, 1944):
        cameraPhotoSizes_JPEG_Exif[0] = [
                (2592, 1944),
                (2048, 1536),
                (1600, 1200),
                (1024, 768),
                (640, 480)
            ]
        DebugPrint("Corrected cameraPhotoSizes_JPEG_Exif[0] to %s" % \
                                    str(cameraPhotoSizes_JPEG_Exif[0]))

    elif phoneModel == "NokiaE7":
        if cameraPhotoSizes_JPEG_Exif[0][0] != (3264, 2448):
            cameraPhotoSizes_JPEG_Exif[0] = [
                (3264, 2448),
                (3264, 1832),
                (2592, 1944),
                (2592, 1456),
                (2048, 1536),
                (1600, 1200),
                (1280, 960),
                (1024, 768),
                (640, 480),
            ]
            DebugPrint( ("Corrected cameraPhotoSizes_JPEG_Exif[0] to %s.\n" % \
                                    str(cameraPhotoSizes_JPEG_Exif[0])) )

        if cameraPhotoSizes_JPEG_Exif[1][0] != (640, 480):
            cameraPhotoSizes_JPEG_Exif[1] = [(640, 480), (320, 240)]
            cameraPhotoSizes_RGB24[1] = [(640, 480), (320, 240)]
            DebugPrint( "Corrected cameraPhotoSizes_RGB24[1] to %s." % \
                                    str(cameraPhotoSizes_RGB24[1]) )

    elif (phoneModel == "NokiaN8") and \
                    (cameraPhotoSizes_JPEG_Exif[0][0] != (4000, 3000)):
        cameraPhotoSizes_JPEG_Exif[0] = [
                (4000, 3000),
                (4000, 2248),
                (3264, 2448),
                (3264, 1832),
                (2592, 1944),
                (2592, 1456),
                (2048, 1536),
                (1600, 1200),
                (1280, 960),
                (1024, 768),
                (640, 480)
            ]

        DebugPrint("Corrected cameraPhotoSizes_JPEG_Exif[0] to %s." % \
                                    str(cameraPhotoSizes_JPEG_Exif[0]))
        """
        !!!!if cameraPhotoSizes_JPEG_Exif[1][0] != (640, 480):
            cameraPhotoSizes_JPEG_Exif[1] = [(640, 480), (320, 240)]
        """
    elif (phoneModel in ["Nokia6120", "NokiaE63"]) and \
                    (cameraPhotoSizes_JPEG_Exif[0][0] != (1600, 1200)):
        cameraPhotoSizes_JPEG_Exif[0] = [
                (1600, 1200),
                (1152, 864),
                (640, 480),
                (320, 240)
            ]

        DebugPrint("Corrected cameraPhotoSizes_JPEG_Exif[0] to %s." % \
                                    str(cameraPhotoSizes_JPEG_Exif[0]))
    """
    #N97, E75
    elif (phoneModel == "NokiaC7") and (cameraPhotoSizes_JPEG_Exif[0][0] != \
                                (2048, 1536)):
        cameraPhotoSizes_JPEG_Exif[0] = [(2048, 1536), (1600, 1200),
                                    (1280, 960), (1024, 768), (640, 480)]

        DebugPrint("Corrected cameraPhotoSizes_JPEG_Exif[0] to %s" %
                                    str(cameraPhotoSizes_JPEG_Exif[0]))
    """
    #!!!!TODO: If we do the correction above, we could get in take_photo() exception "ValueError: Size not supported for camera"
    #!!!!TODO: think how to do exactly. Maybe do again GeneralUseCameraS60(0), (1) with the right orientation!!!! Or maybe take out the check in camera2.take_photo(). Or, in iCam.TakePhotoAndUpload() if you get exception use the next resolution.
    #camera.device[0].image_size(modeMap[mode]) ~= cameraPhotoSizes_JPEG_Exif[0]
    #camera.device[1].image_size(modeMap[mode]) ~= cameraPhotoSizes_JPEG_Exif[1]


def MainSetCameraParams():
    global sensorsAvailable
    #global accSensor, rotSensor
    global numCamerasSupported
    global cameraPhotoSizes_JPEG_Exif, cameraPhotoSizes_RGB24
    global cameraVideoFormats, cameraVideoFrameSizes, cameraVideoModes
    global localVideoModeIndex
    global localVideoMode


    DebugPrint("Entered MainSetCameraParams(): camera params and init sensors.")

    # Every (Symbian) phone should support this resolution. !!!!TODO: differentiate if phone has 0, 1 or 2 cameras
    localVideoMode = [((176, 144), 15.0), ((176, 144), 15.0)]

    if ANDROID_OS:
        sensorsAvailable = ""

        numCamerasSupported = 1
        cameraPhotoSizes_JPEG_Exif = [[(320, 240)], []]
        cameraPhotoSizes_RGB24 = [[(320, 240)], []]
        # localPhotoResolutionIndex[0]

    elif SYMBIAN_S60_OS:
    # elif SYMBIAN_OS:
        try:
            if logAccelerometerAndRotationSensors:
                if phoneModel in ["NokiaN95", "NokiaN82"]:
                    accSensor = sensor.Sensor(sensor.sensors()["AccSensor"]["id"],
                                        sensor.sensors()["AccSensor"]["category"])

                    accSensor.connect(AccSensorCallback)
                    rotSensor = sensor.Sensor(sensor.sensors()["RotSensor"]["id"],
                                        sensor.sensors()["RotSensor"]["category"])

                    rotSensor.connect(RotSensorCallback)

            SetCameraParamsS60()
        except:
            DebugPrintErrorTrace()

    elif SYMBIAN_UIQ_OS:
        sensorsAvailable = ""

        # if phoneModel in ["Nokia 6120", "Nokia N95"]:
        #    SetUIOrientation(0, True)
        numCamerasSupported = 1
        cameraPhotoSizes_JPEG_Exif = [[(320, 240)], []]
        cameraPhotoSizes_RGB24 = [[(320, 240)], []]
        # localPhotoResolutionIndex[0]

    elif iOS_PYOBJC:
        sensorsAvailable = ""

        # numCamerasSupported = 0
        numCamerasSupported = 1
        cameraPhotoSizes_JPEG_Exif = [[(320, 240)], []]
        cameraPhotoSizes_RGB24 = [[(320, 240)], []]
        # localPhotoResolutionIndex[0]

    elif WINDOWS_CE_OS_PYTHONCE:
        sensorsAvailable = ""

        # numCamerasSupported = 0
        numCamerasSupported = 1
        cameraPhotoSizes_JPEG_Exif = [[(320, 240)], []]
        cameraPhotoSizes_RGB24 = [[(320, 240)], []]
        # localPhotoResolutionIndex[0]

    elif RASPBIAN_OS:
        sensorsAvailable = ""

        # numCamerasSupported = 0
        numCamerasSupported = 1
        cameraPhotoSizes_JPEG_Exif = [[(320, 240)], []]
        cameraPhotoSizes_RGB24 = [[(320, 240)], []]
        # localPhotoResolutionIndex[0]


    # localVideoMode = [((320, 240), 15.0), ((176, 144), 15.0)]
    # localVideoMode = [(640, 480), (176, 144)]
    """
    #!!!!
    #camera.release()
    ##camera._my_camera = camera._camera.Camera(0)
    #camera.UseCamera(0)
    GeneralUseCameraS60(0)
    """

    if numCamerasSupported == 0:
        cameraVideoFormats[0] = []
        cameraVideoFrameSizes[0] = []
        cameraVideoModes[0] = []
    else:
    #if numCamerasSupported >= 1:
        if cameraVideoFormats[0] == []:
            cameraVideoFrameSizes[0] = [(176, 144)]
            cameraVideoModes[0] = [{"rate": 15.0, "size": (176, 144)}]

    if numCamerasSupported == 2:
        if cameraVideoFormats[1] == []:
            cameraVideoFrameSizes[1] = [(176, 144)]
            cameraVideoModes[1] = [{"rate": 15.0, "size": (176, 144)}]
    else:
    #if numCamerasSupported == 1:
        cameraVideoFormats[1] = []
        cameraVideoFrameSizes[1] = []
        cameraVideoModes[1] = []

    """
    In case something changed (e.g., on S60 camera2 module doesn't exist), we
        make sure the indices are within the valid values.
        TODO!!!! This check is repeated in RecordConfigMenu() so REUSE CODE.
    """
    global localVideoModeIndex
    for i in range(2):
        if (localVideoModeIndex[i] < 0) or \
                        (localVideoModeIndex[i] >= len(cameraVideoModes[i])):
            localVideoModeIndex[i] = 0

    ###########################################################################
    ###########################################################################
    ##########################END CAMERA PARAMETER SET#########################
    ###########################################################################
    ###########################################################################


def MainLoadStateAndConfig():
    global accessPointName
    global stateLoaded

    try:
        DebugPrint("MainLoadStateAndConfig(): Reading configuration.")

        #!!!!TODO: we load again the state, but now we have std*.txt created.
        if LoadStateFromFile(STATE_PATH_FILENAME):
            DebugPrint("MainLoadStateAndConfig(): Read configuration from %s " \
                        "(or its backup if this file doesn't exist)." % \
                        STATE_PATH_FILENAME)

            """
            if (deviceId == IMEI_6120) or (deviceId == IMEI_6680):
                global bluetoothServerAddress
                #bluetoothServerAddress = BT_ADDR_N95
                bluetoothServerAddress = INTERNET_PROXY_PHONE_BLUETOOTH_ADDRESS
            """
        else:
            DebugPrint("MainLoadStateAndConfig(): LoadStateFromFile() " \
                        "ret False.")
            # Preserve this order of initializing the state variables.

            # if deviceId == IMEI_6120: #Nokia 6120
            # if deviceId == IMEI_N95: #Nokia N95
            if deviceId == INTERNET_PROXY_PHONE_DEVICE_ID:
                if accessPointName == u"":
                    # accessPointName = u"RDSPP"
                    accessPointName = u"RDS"
                    DebugPrint("MainLoadStateAndConfig(): accessPointName " \
                            "is empty so made it %s." % str(accessPointName))
            # else:
            #    accessPointName = u""

        LoadLocalConfigFromFile(LOCAL_CONFIG_PATH_FILENAME)

        if bluetoothMode == 1: # BT server
            LoadBtMsgMostRecentTime()

        DebugPrint("Exiting MainLoadStateAndConfig().")
    except:
        DebugPrintErrorTrace()

    stateLoaded = True


def Main():
    global LOCAL_FOLDER
    # global myTimer
    global localPhotoResolution, localVideoMode, localQualityIndex
    global photoResolutionStr, photoModeStr, pauseIntervalStr, \
        exposureStr, whiteBalanceStr, flashStr
    global digitalZoom, photoResolutionIndex, \
        localPhotoResolutionIndex, photoModeIndex, photoQuality, \
        pauseInterval, reactiveLoopOpsIndex , exposureIndex, \
        whiteBalanceIndex, flashIndex
    global audioRecordDuration, videoRecordDuration, rotateDegreesImage
    global mobileCountryCode, mobileNetworkCode, locationAreaCode, \
        cellId  # GSM location info
    global signalStrength, signalUnits
    global accessPointName, bluetoothMode
    global gpsInfo, readGPS
    global cameraPhotoSizes_JPEG_Exif, cameraPhotoSizes_RGB24
    global cameraVideoFormats, cameraVideoFrameSizes, cameraVideoModes
    global myMaxRamdriveSize
    global numCamerasSupported
    global deviceId, phoneModel, orientationForThisPhoneModel
    global pyS60VersionNumber
    global bluetoothSelfAddress, bluetoothSelfName
    global accessPointRetryConnect
    global startButtonPressed, startAutomatically
    global STATE_PATH_FILENAME
    global sensorsAvailable
    global stateLoaded
    global uploadMediaToYouTube, uploadMediaToPicasa, useiCamServer
    global MY_DEBUG_UPLOAD_MSG
    global OSName

    DebugPrint("Entered Main().")

    #EraseOldestFilesAndMessages()
    if False:
        EraseOldestFilesFromFolderWithFileCountQuota(LOCAL_FOLDER,
                                                    filterString="stdout_",
                                                    fileCountQuota=100)
        EraseOldestFilesFromFolderWithFileCountQuota(LOCAL_FOLDER,
                                                    filterString="stderr_",
                                                    fileCountQuota=100)

    MainLoadStateAndConfig()

    LoadMDExtensionScript()

    global OSName

    if ANDROID_OS:
        OSName = "Android"
    elif SYMBIAN_S60_OS:
        # Put S60 as well.
        OSName = "Symbian"
    elif SYMBIAN_UIQ_OS:
        OSName = "Symbian UIQ"
    elif iOS_PYOBJC:
        OSName = "iOS"
    elif UNIX_OS:
        OSName = "UNIX"
    elif WINDOWS_OS:
        OSName = "Windows"
    elif WINDOWS_CE_OS_PYTHONCE:
        #OSName = "Windows"
        OSName = "WinCE"

    phoneModel = GetPhoneModel()

    # Here, the orientationForThisPhoneModel could be wrong.
    #MainLog2()

    if ANDROID_OS:
        #myRes = DialogGetInput("blabla", "blamore", "blaInit")
        #DisplayNote(myRes)
        pass
    elif SYMBIAN_OS:
        SetupSymbian()

        """
        while True:
            e32.ao_sleep(1.0)
        """

    #LoadMDExtensionScript()

    # DisplayNote("Testing.")
    #phoneModel = GetPhoneModel()

    MainSetUI()

    # We do this to try to avoid having issues with the read camera resolution:
    SleepAndPetWatchdog(2.0)
    """
    MainSetCameraParams() needs to be after MainSetUI() because MainSetUI()
        sets the orientation (on S60) and otherwise the read resolutions
        could be wrong.
    """
    MainSetCameraParams()

    MainLog2()

    #MainSetCameraParams()

    if False: #deviceId == IMEI_E7:
        # We start some profiling

        """
        # Doesn't work on PyS60: - gives exception:
        #    profile.py, line 462, in runctx
        #    NameError: name 'GetGSMLocation' is not defined
        profile.run("GetGSMLocation()")

        #profile.run("print 1")
        """
        pr = profile.Profile()
        pr.runcall(GetGSMLocation)

        filename = "profiling_data_iCam"
        #filename = None
        if filename is not None:
            pr.dump_stats(filename)

        """
        myProfile = hotshot.Profile("hotshot_stats")
        myProfile.runcall(GetGSMLocation)
        myProfile.close()
        """
    else:
        GetGSMLocation()


    if ANDROID_OS:
        myMaxRamdriveSize = -1
        signalStrength = -1
        signalUnits = "dbm"
        accessPointName = u"[DEFAULT_AP]"
        localPhotoResolutionIndex = [-1, -1]
    elif SYMBIAN_S60_OS:
    # elif SYMBIAN_OS:
        try:
            myMaxRamdriveSize = sysinfo.max_ramdrive_size()
        except:
            DebugPrintErrorTrace()
            myMaxRamdriveSize = -1

        try:
            signalStrength = sysinfo.signal_dbm()
            signalUnits = "dbm"
        except:
            DebugPrintErrorTrace()
            try:
                signalStrength = sysinfo.signal_bars()
                signalUnits = "bars"
                if signalStrength < 0:
                    signalStrength = NO_GSM_SIGNAL_STRENGTH
            except:
                signalStrength = NO_GSM_SIGNAL_STRENGTH
                signalUnits = "none"
                # accessPointName = u""
                accessPointRetryConnect = True
                DebugPrintErrorTrace()
    elif SYMBIAN_UIQ_OS:
        myMaxRamdriveSize = -1
        signalStrength = -1
        signalUnits = "dbm"
        accessPointName = u"[DEFAULT_AP]"
        localPhotoResolutionIndex = [-1, -1]
    elif iOS_PYOBJC:
        myMaxRamdriveSize = -1
        signalStrength = -1
        signalUnits = "dbm"
        accessPointName = u"[DEFAULT_AP]"
        localPhotoResolutionIndex = [-1, -1]
    elif UNIX_OS:
        myMaxRamdriveSize = -1
        signalStrength = -1
        signalUnits = "dbm"
        numCamerasSupported = 0
        accessPointName = u"Linux_PC_Internet_Connection"
        localPhotoResolutionIndex = [-1, -1]
    elif WINDOWS_OS:
        myMaxRamdriveSize = -1
        signalStrength = -1
        signalUnits = "dbm"
        numCamerasSupported = 0
        accessPointName = u"Windows_PC_Internet_Connection"
        localPhotoResolutionIndex = [-1, -1]

        """
        YouTubeVideoUploadThroughProxy(pathFileName = "./CurrentSymLink_1.3gp",
                fileName = "CurrentSymLink_1.3gp", aKeyword = "myDeviceId",
                crtTime = None, mediaTimeStr = "", mediaDateStr = "",
                deviceId = "", cameraId = 0)
        return
        """

        """
        res = UploadStateAndFileAndStoreState("[NOT_USED_DEVICE_ID]", 0,
                    "VIDEO0900.mp4", "./VIDEO0900.mp4", ICAM_SERVER_NAME,
                    WEBPAGE_UL_GZIPPED_STATE_AND_FILE)
        return
        """
    elif WINDOWS_CE_OS_PYTHONCE:
        myMaxRamdriveSize = -1
        signalStrength = -1
        signalUnits = "dbm"

        accessPointName = u"[DEFAULT_AP]"
        localPhotoResolutionIndex = [-1, -1]


    ##!!!!!!!Put all localPhotoResolutionIndex = [-1, -1] above and the code below in MainSetCameraParams()
    # JPEG_Exif for Main camera and RGB24 for VGA camera.
    photoModeIndex = [3, 2]
    """
    if phoneModel in ["Nokia6120", "NokiaN95", "NokiaN82", "NokiaN8"]:
        # JPEG_Exif for Main camera and RGB24 for VGA camera.
        photoModeIndex = [3, 2]
    elif phoneModel == "Nokia6680":
        # RGB24 for Main camera and RGB24 for VGA camera
        #photoModeIndex = [2, 2]

        # For Nokia 6680 JPEG_Exif for Main camera and RGB24 for VGA
        #    camera - JPEG_Exif and 1280x960 gives out of mem exception (but it
        #    appears RGB24 and 1280x960 does not)- see!!!!
        #   Logs\Nokia6680\34_JPEG_Exif_imageMode_rear_capture_error_Out_of_mem
        photoModeIndex = [3, 2]
    else:
        # For Nokia 6680 JPEG_Exif for Main camera and RGB24 for VGA
        #   camera - JPEG_Exif and 1280x960 gives out of mem exception (but it
        #   appears RGB24 and 1280x960 does not)- see!!!!
        #   Logs\Nokia6680\34_JPEG_Exif_imageMode_rear_capture_error_Out_of_mem
        photoModeIndex = [3, 2]
    """

    """
    On Nokia 6680, setting the photo mode when calling take_photo() returns:
        KErrNoMemory for RGB12
        "Size not supported for camera" if we give RGB16
        "Size not supported for camera" if we give JPEG_Exif

    RGB24 gives error
        for pic.resize (from SetDigitalZoom)
            SymbianError: [Errno -4] Error resizing image:KErrNoMemory
        OR
        for take_photo: SymbianError: [Errno -4] KErrNoMemory
    """

    """
    if GetFreeRAM() < cameraPhotoSizes[0][0] * cameraPhotoSizes[0][1] * 3 * 2:
        # If we have less than 6MB RAM use 2nd biggest resolution (for Nokia
        #   6680, we had ~5MB free and when trying to capture at biggest
        #   res - 1280x968 - and resizing it was running out of memory).
        #if GetFreeRAM() < 6 * 1024 * 1024:
        localPhotoResolutionIndex = [1, len(cameraPhotoSizes) - 1]

        # On Nokia 6680, using localPhotoResolutionIndex[0] = 0 (i.e., 1280x960
        #   photo resolution for Main camera) makes take_photo give error:
        #   "SymbianError: [Errno -4] KErrNoMemory" even if we have 6.1MB free
        #   on the C drive.
        # Or: "SymbianError: [Errno -4] Error resizing image:KErrNoMemory"
        # See Logs\Nokia6680\14, 15 and 16
    else:
        # The resolution indices for the Main (index 0) and VGA (index 1)
        #   camera, pointing at cameraPhotoSizes.
        localPhotoResolutionIndex = [0, len(cameraPhotoSizes) - 1]
    """

    if numCamerasSupported == 1:
        # The resolution indices for the Main (index 0) and VGA (index 1)
        #   camera, pointing at cameraPhotoSizes.
        localPhotoResolutionIndex = [0, -1]
    elif numCamerasSupported == 2:
        # The resolution indices for the Main (index 0) and VGA (index 1)
        #   camera, pointing at cameraPhotoSizes.
        localPhotoResolutionIndex = [0, len(cameraPhotoSizes_RGB24[1]) - 1]

    DebugPrint("Main(): localPhotoResolutionIndex = %s" % \
                            str(localPhotoResolutionIndex))

    """
    try:
        UploadUnsentFILES()
    except:
        DebugPrintErrorTrace()
    """

    """
    try:
        # Basically telephone module can't be used on S60 2nd ed.
        #   phone_ext should be used instead.
        #See http://discussion.forum.nokia.com/forum/showthread.php?120891-Bind-an-incoming-call
        #  "For 2nd edition it should be more difficult because there's no
        #       official function available for this.
        #   Try this : download SMSCoop in it there's a phone_ext.pyd
        #       extension. Install it in your phone.
        #   This extension provide similar functionalities."
        telephone.call_state(TelephoneCallback)
        #telephone.incoming_call()
    except: #AttributeError ImportError:
        exceptionType, exceptionValue, exceptionTraceback = sys.exc_info()
        myText = "The telephone module does not work - most likely because " \
                    "you are using a 2nd edition S60 cellphone - reported " \
                    "to have issues with the telephone module. "\
                    "Exception details: %s." \
                    % repr( traceback.format_tb(exceptionTraceback) )

        if MY_DEBUG_UPLOAD_MSG:
            UploadGZippedData(deviceId, myText, ICAM_SERVER_NAME,
                                WEBPAGE_UL_GZIPPED_TEXT, None)

        DebugPrint(myText)

        DebugPrintErrorTrace()
    """
    # #########################################################################
    # #########################################################################
    # ##########    By now the iCam UI and device params are set up. ##########
    # #########################################################################
    # #########################################################################

    MainConnectivity()
    # #########################################################################
    # ########NOW PHONE SHOULD HAVE CONNECTIVITY - 3G and/or Bluetooth#########
    # ###IMPORTANT: DO NOT UPLOAD OR DOWNLOAD ANYTHING BEFORE HERE, BECAUSE####
    # ################IT MIGHT POP A MENU ASKING FOR ACCESS POINT##############
    # #########################################################################

    
    global sentBTMessageTo6680
    if sentBTMessageTo6680 == 1:
        """
          Since it is possible that iCam is stopped when power comes back and
        we miss this event we have saved as part of state sentBTMessageTo6680
        in order to check and act, if necessary.
        """
        ExecuteCommands("send-command-via-bluetooth " + BT_ADDR_6680 + \
                        " set-pause-interval 240")
        sentBTMessageTo6680 = 0
        StoreState()

    MainLog()

    MainTestCommands()

    MainStartReactiveLoops()


def MainConnectivity():
    global accessPointName, accessPointRetryConnect
    global stateLoaded

    DebugPrint("Entered MainConnectivity().")

    stateLoaded = True
    # if SYMBIAN_OS:
    if SYMBIAN_S60_OS:
        DisplayRedrawInfo()


    # #########################################################################
    # #########################################################################
    # ####    We set the bluetoothMode, and connect to the access point, if ###
    # ##### appropriate - note that AP connection takes little on Symbian.#####
    # ###### We also download any commands and execute them ASAP, at the ######
    # ##### beginning - useful to prevent erroneous modes #####################
    # #########################################################################
    # #########################################################################
    if bluetoothMode == -1:
    # if bluetoothServerAddress == "":
        SelectBluetoothMode()
    elif bluetoothMode == 1:
        # BluetoothServer
        BluetoothServerInitialize()
    elif bluetoothMode == 2:
        # BluetoothClient.
        BluetoothClientDiscoverServer()
        BluetoothClientInitializeInbox()

        # This (also) has the effect of downloading commands received via BT
        #   - equivalent to DownloadCommands().
        #if WINDOWS_OS == False:
        if WINDOWS_OS:
            pass
        else:
            BluetoothMessageListProcess(processJustNonSMF_BtMsgs=True)

    SetLocalPhotoResolution()

    """
    accessPointName = u""
    accessPointRetryConnect = True
    """
    if bluetoothMode == 2:
        """
        or signalStrength == NO_GSM_SIGNAL_STRENGTH:

        !!!!Remember or figure out when was the case 
            signalStrength == NO_GSM_SIGNAL_STRENGTH
          useful.

        It is not a good idea to check also
            signalStrength == NO_GSM_SIGNAL_STRENGTH because
            we can arrive in this case when there is no valid SIM card
            or no SIM card at all.
        """
        DebugPrint("Main(): Setting accessPointRetryConnect = True.")

        accessPointRetryConnect = True
    else:
        # We can choose a WiFI or 3G Acess Point:
        SelectAccessPointIfNoneAvailableAndConnectToIt()

        # This function doesn't really connect to the Internet and works fast.

        # thread.start_new_thread(
        #        SelectAccessPointIfNoneAvailableAndConnectToIt, ())
        if deviceId == IMEI_E7:
            appuifw.note(u"Connected to AP!", "info")

    if bluetoothMode != 2: # ~IMPORTANT: we want to prevent to try to connect to the Internet
        # TODO: Maybe we should synchronize more than once per run.
        TimeSyncNTP()

    if False:
        # We attempt to synchronize the time with the iCam server:
        if useiCamServer > 0:
            TimeSyncWithiCamServer()


myTextInit = ""
def MainLog2():
    global myTextInit

    # myText1 = "Started application on %s." % datetime.datetime.now().ctime()
    try:
        myText = "<br/><br/>Started application on %s - %s version. " \
                    "Release time: %s. " % \
                    (GetCurrentDateTimeStringNice(), OSName,
                            CURRENT_RELEASE_TIME)
    except:
        myText = ""
        DebugPrintErrorTrace()

    try:
        myText += "Python version: %s. " % str(sys.version_info)
    except:
        DebugPrintErrorTrace()

    try:
        myText += "deviceId = %s. phoneNumber = %s. " % (deviceId, phoneNumber)

        """
        #From http://stackoverflow.com/questions/787776/find-free-disk-space-in-python-on-os-x
            (see also http://ubuntuforums.org/showthread.php?t=961505):

        if ANDROID_OS:
            myStatVFS = os.statvfs("/sdcard")
        elif SYMBIAN_OS:
            myStatVFS = os.statvfs(r"C:")

        myText += "Free drive space %d." % ((myStatVFS.f_bavail *
                    myStatVFS.f_frsize) / 1024)

        #Java code from http://android.bigresource.com/Track/android-7gosN9ZDf/:
        #StatFs stat = new StatFs(
        #       Environment.getExternalStorageDirectory().getAbsolutePath());

        #stat.restat(
        #       Environment.getExternalStorageDirectory().getAbsolutePath());
        #long available = ((long) stat.getAvailableBlocks() *
        #           (long) stat.getBlockSize());
        """
    except:
        DebugPrintErrorTrace()

    try:
        if ANDROID_OS:

            def ShowMap(myMap):
                try:
                    res = ""
                    for (keyIter, valueIter) in myMap.iteritems():
                        # print k,"=",v
                        res += "%s = %s" % (keyIter, valueIter)
                except:
                    DebugPrintErrorTrace()

            myText1 = ""
            try:
                myText1 += "Build" \
                    + ShowMap(myDroid.getConstants("android.os.Build").result)
            except:
                DebugPrintErrorTrace()

            try:
                myText1 += "Version" + ShowMap(myDroid.getConstants(
                                "android.os.Build$VERSION").result)
            except:
                DebugPrintErrorTrace()

            try:
                myText1 += "Model" + ShowMap(myDroid.getConstants(
                                "android.os.Build$MODEL").result)
            except:
                DebugPrintErrorTrace()
            # DisplayNote(myText1)

            myText += myText1 + "phoneModel = %s. Firmware version = %s.\n" % \
                            (str(phoneModel),
                               str(myDroid.getDeviceSoftwareVersion().result))

        elif SYMBIAN_S60_OS:
        # elif SYMBIAN_OS:
            myText += "phoneModel = %s. Firmware version = %s. " \
                        "OS = Symbian version %s.\n" % \
                        (str(phoneModel), sysinfo.sw_version(),
                           sysinfo.os_version())
        elif WINDOWS_CE_OS_PYTHONCE:
            myText += "phoneModel = %s. OS = WinCE.\n" % str(phoneModel)
        else:
            pass
    except:
        DebugPrintErrorTrace()

    try:
        myText += "sys.platform = %s. " % sys.platform
    except:
        DebugPrintErrorTrace()

    try:
        if ANDROID_OS:
            myText += "Python for Android version %s.\n" % "[NOT_AVAILAVABLE]"
        elif SYMBIAN_OS:
        # elif SYMBIAN_OS and SYMBIAN_S60_OS:
            myText += "S60 version %s, pyS60VersionNumber = %d. " \
                        "PyS60 version info = %s.\n" % \
                        (S60_EDITION, pyS60VersionNumber,
                           e32.pys60_version_info)
        elif WINDOWS_CE_OS_PYTHONCE:
            myText += "PythonCE version %s.\n" % "[NOT_AVAILAVABLE]"
    except:
        DebugPrintErrorTrace()

    try:
        myText += "bluetoothSelfAddress = %s. bluetoothSelfName = %s. " \
                    "bluetoothMode = %d. \n" % \
                    (bluetoothSelfAddress, bluetoothSelfName, bluetoothMode)
    except:
        DebugPrintErrorTrace()

    try:
        myText += "pauseInterval = %d. " \
                "pauseIntervalGdata = %d. " \
                "reactiveLoopOpsIndex = %d. " \
                "sentBTMessageTo6680 = %d. " \
                "numCamerasSupported = %d. cameraMode[0] = %d. " \
                "cameraMode[1] = %d. orientationForThisPhoneModel = %s.\n" % \
                (
                    pauseInterval,
                    pauseIntervalGdata,
                    reactiveLoopOpsIndex,
                    sentBTMessageTo6680,
                    numCamerasSupported,
                    cameraMode[0], cameraMode[1],
                    orientationForThisPhoneModel)

        if numCamerasSupported > 0:
            # if SYMBIAN_OS:
            if SYMBIAN_S60_OS:
                myText += "Main camera: possible photo resolutions: for " \
                            "JPEG_Exif %s; for RGB24 %s. " % \
                            (cameraPhotoSizes_JPEG_Exif[0],
                               cameraPhotoSizes_RGB24[0])

                myText += "image_modes() = %s. flash_modes() = %s, " \
                            "max_zoom() = %d, exposure_modes() = %s, " \
                            "white_balance_modes() = %s.\n" % \
                            (camera.image_modes(), camera.flash_modes(),
                                camera.max_zoom(), camera.exposure_modes(),
                                camera.white_balance_modes())

                if numCamerasSupported == 2:
                    """
                    camera.release()
                    #camera._my_camera = camera._camera.Camera(1)
                    camera.UseCamera(1)
                    """

                    myText += "VGA camera: possible photo resolutions: for " \
                                "JPEG_Exif %s, RGB24 %s.\n" % \
                                (cameraPhotoSizes_JPEG_Exif[1],
                                    cameraPhotoSizes_RGB24[1])

                    if camera2IsImported:
                        myText += "image_modes() = %s. flash_modes() = %s, " \
                                    "max_zoom() = %d, exposure_modes() = %s, "\
                                    "white_balance_modes() = %s. \n" % \
                                    (camera.image_modes(
                                            camera.device[1].image_modes()),
                                        camera.flash_modes(
                                            camera.device[1].flash_modes()),
                                        camera.max_zoom(),
                                        camera.exposure_modes(
                                            camera.device[1].exposure_modes()),
                                        camera.white_balance_modes(
                                       camera.device[1].white_balance_modes()))
                    else:
                        """
                        !!!!Should switch to the VGA camera
                        #myText += "image_modes() = %s. flash_modes() = %s, " \
                        #           "max_zoom() = %d, exposure_modes() = %s, "\
                        #           "white_balance_modes() = %s. "
                        #           % (camera.image_modes(),
                        #               camera.flash_modes(),
                        #               camera.max_zoom(),
                        #               camera.exposure_modes(),
                        #               camera.white_balance_modes())
                        """
                        pass

                    """
                    camera.release()
                    #camera._my_camera = camera._camera.Camera(0)
                    camera.UseCamera(0)
                    """
            else:
                pass
    except:
        DebugPrintErrorTrace()

    # Here we were setting the video recording configuration for camera 0.

    if SYMBIAN_OS:
        try:
            if camera2IsImported:
                myText += myTextMainCamera2Video
        except:
            DebugPrintErrorTrace()


    try:
        # if SYMBIAN_OS:
        if SYMBIAN_S60_OS:
            myText += "sensorsAvailable = %s. Active profile = %s. " \
                        "ring_type = %s. \n" % \
                        (sensorsAvailable, sysinfo.active_profile(),
                            sysinfo.ring_type())
        else:
            pass
    except:
        DebugPrintErrorTrace()

    try:
        # if SYMBIAN_OS:
        if SYMBIAN_S60_OS:
            # It seems always GetFreeDriveSpace("D:") = free_ram().
            myText += "total_ram = %d, max_ramdrive_size = %d, " \
                        "free_ram = %d. total_rom = %d. Free space in " \
                        "bytes on drive C = %d, on drive D = %d, on drive " \
                        "E = %d. \n" % \
                        (sysinfo.total_ram(), myMaxRamdriveSize,
                            GetFreeRAM(), sysinfo.total_rom(),
                            GetFreeDriveSpace("C:"), GetFreeDriveSpace("D:"),
                            GetFreeDriveSpace("E:"))

            if misoIsImported:
                myText += "miso: num_alloc_heap_cells() = %d, " \
                            "num_free_heap_cells() = %d, " \
                            "alloc_heap_cells_size() = %d, " \
                            "heap_total_avail() = %d, " \
                            "heap_biggest_avail() = %d, " \
                            "heap_base_address() = %d, stack_info() = %s. \n" % \
                            (miso.num_alloc_heap_cells(),
                                miso.num_free_heap_cells(),
                                miso.alloc_heap_cells_size(),
                                miso.heap_total_avail(),
                                miso.heap_biggest_avail(),
                                miso.heap_base_address(),
                                str(miso.stack_info()))
        elif ANDROID_OS:
            myText += "Free space in bytes on LOCAL_DRIVE = %d. \n" % \
                        GetFreeDriveSpace(LOCAL_DRIVE)
    except:
        DebugPrintErrorTrace()

    try:
        myText += "battery = %d. charger_status = %d. \n" % \
                    (GetBatteryLevelPercentage(), GetChargerStatus())

        myText += "MY_DEBUG_STDOUT = %d, MY_DEBUG_STDERR = %d, " \
                    "MY_DEBUG_STDERR_2 = %d, MY_DEBUG_UPLOAD_MSG = %d. \n" % \
                    (MY_DEBUG_STDOUT, MY_DEBUG_STDERR, MY_DEBUG_STDERR_2,
                       MY_DEBUG_UPLOAD_MSG)

        myText += "LOCAL_FOLDER = %s, LOCAL_FOLDER_MEDIA_FILES = %s.\n" \
                    "STDOUT file name = %s. STDERR file name = %s.\n" % \
                    (LOCAL_FOLDER, LOCAL_FOLDER_MEDIA_FILES, stdoutFileName,
                       stderrFileName)

        myText += "startAutomatically = %d. \n" % \
                    startAutomatically

        myText += "uploadMediaToYouTube = %d, uploadMediaToPicasa = %d, " \
                    "useiCamServer = %d. " % \
                    (uploadMediaToYouTube, uploadMediaToPicasa,
                       useiCamServer)
        """
        myText += "uploadMediaToYouTube = %d, uploadMediaToPicasa = %d, " \
                    "useiCamServer = %d, USE_ICAM_SERVER = %d." \
                    % (uploadMediaToYouTube, uploadMediaToPicasa,
                       useiCamServer, USE_ICAM_SERVER)
        """

        myText += "internetUploadMaxErrors = %d. " \
                    "QoS params: storeLocallyMedia = %d, " \
                    "saveUnsentPackets = %d, " \
                    "SOCKET_DEFAULT_TIMEOUT = %.2f, uploadUnsentData = %d, " \
                    "uploadHowManyOfLatestBluetoothMessages = %d, " \
                    "MODE_FOR_PHONE_WITH_LITTLE_RAM_AND_UNRELIABLE_MEM_CARD = %d, " \
                    "ERASE_ORIGINAL_MEDIA_FILE_AFTER_READ = %d. " % \
                    (
                        internetUploadMaxErrors,
                        storeLocallyMedia,
                        saveUnsentPackets,
                        SOCKET_DEFAULT_TIMEOUT,
                        uploadUnsentData,
                        uploadHowManyOfLatestBluetoothMessages,
                        MODE_FOR_PHONE_WITH_LITTLE_RAM_AND_UNRELIABLE_MEM_CARD,
                        ERASE_ORIGINAL_MEDIA_FILE_AFTER_READ
                    )

        myText += "ICAM_SERVER_NAME = %s.\n" % ICAM_SERVER_NAME

        myText += "BATTERY_LEVEL_THRESHOLD = %d.\n" % BATTERY_LEVEL_THRESHOLD

        myText += "localPhotoResolution = %s.\n" % str(localPhotoResolution)

        myText += "localVideoMode = %s; localVideoModeIndex = %s.\n" % \
                    (str(localVideoMode), str(localVideoModeIndex))

        # if SYMBIAN_OS:
        if SYMBIAN_S60_OS:
            myText += "displaySize = %s.\n" % str(sysinfo.display_pixels())
            # displaySize
        else:
            pass
    except:
        DebugPrintErrorTrace()

    myTextInit = myText
    DebugPrint(myText)


def MainLog():
    global localVideoMode
    global cameraVideoFormats, cameraVideoFrameSizes, cameraVideoModes

    DebugPrint("Entered MainLog().")

    # if SYMBIAN_OS:
    if SYMBIAN_S60_OS or ANDROID_OS:
        if googleUsername is None:
            SelectServersMenu()

    if bluetoothMode != 2: # A BT client does NOT have Internet access
        if deviceId == IMEI_E7:
            """
            For the first access to the Internet we create a separate thread
                and wait (but keep the UI responsive), since this access could
                take long - it sets up the AP connection, etc.
              The downloadCommandsCounter gets incremented/reset after every
                Inet access.
            """

            """
            We can set firstTime=-1 on SYMBIAN_OS, but it's required to be bool
                on ANDROID_OS.
            """
            SetMenu(firstTime=-1)

            # This assignment has to be put before MyThreadStart(DownloadCommands)
            crtDownloadCommandsCounter = downloadCommandsCounter

            """
            We can execute DownloadCommands in a separate thread or
                NOT (in case we run PyS60 1.4.5).
            """
            MyThreadStart(DownloadCommands)

            DebugPrint("MainLog(): Waiting for the DownloadCommands() " \
                        "thread to finish.")

            """
            This primitive synchronization is OK - even if for some strange
                reason downloadCommandsCounter changes before the other thread
                finishes, we get out from waiting in this thread and proceed
                without having some race conditions (I think).
            Worst case we end up waiting quite a lot (once it was ~2 minutes?? :o )
                until the while loop ends, even if we have SOCKET_DEFAULT_TIMEOUT = 20 -
                see Z:\1PhD\ReVival\Logs\NokiaE7\2013_06_23_7_interesting_maybe_sync_issues\stdout_2013_06_23_14_44_26.txt.
            """
            while crtDownloadCommandsCounter == downloadCommandsCounter:
                SleepAndPetWatchdog(1.0)
                #e32.ao_yield()

            DebugPrint("MainLog(): DownloadCommands() thread finished.")

            #SetMenu(firstTime=True)
            SetMenu(firstTime=False)
        else:
            """
            def DownloadCommmandsThread():
                DownloadCommands()
            #thread.start_new_thread(DownloadCommmandsThread, ())
            MyThreadStart(DownloadCommmandsThread)
            """
            DownloadCommands()

    # Otherwise the application gets blocked at .sms_send()
    #if signalStrength != NO_GSM_SIGNAL_STRENGTH:
    #if deviceId == IMEI_6120: #Nokia 6120
    #if deviceId == INTERNET_PROXY_PHONE_DEVICE_ID:
    if accessPointName == u"RDSPP":
        CommunicateWithOperator()
        """
        if accessPointRetryConnect:
            CommunicateWithOperator()
        """

    if MY_DEBUG_UPLOAD_MSG:
        # On WINDOWS_OS, this can give exception because BLUETOOTH_INBOX_PATH == None. This is acceptable...
        # UploadText(myText, ICAM_SERVER_NAME, WEBPAGE_UL_GZIPPED_TEXT)
        UploadGZippedData(deviceId, myTextInit, ICAM_SERVER_NAME,
                          WEBPAGE_UL_GZIPPED_TEXT, None)

    global STORE_STATE
    STORE_STATE = False

    try:
        SetUploadedPhotoResolutionIndex(photoResolutionIndex)()
        SetPhotoQuality(photoQuality)()

        # Can take it out, since the implementation does not change anything from
        #   the menu.
        SetPauseInterval(pauseInterval)()

        # e32.ao_sleep(5)
        SetDigitalZoom(digitalZoom)()
    except:
        DebugPrintErrorTrace()

    STORE_STATE = True

    SetMenu()
    StoreState()

    """
    DebugPrint("Main(): Calling myTimer.after(..., ReactiveLoop).")

    # To avoid getting "Photo request ongoing" exception, because the
    #   view-finder is started in SetDigitalZoom, but SetPauseInterval calls a
    #   take_photo().
    #e32.ao_sleep(1)
    try:
        # Wait for 10 seconds - Nokia 6680 takes long to start the application.
        myTimer.after(10, ReactiveLoop)
    except:
        DebugPrint("Main(): myTimer.after(..., ReactiveLoop) returned " \
                    "exception.")
        DebugPrintErrorTrace()
    """

    """
    #if phoneModel in ["NokiaE7", "NokiaN8"]:
    if SYMBIAN_3:
        SetUIOrientation(1)
    """


def MainTestCommands():
    global bluetoothMode

    return

    """
    # ######################A TEST - EXOTIC COMMANDS, ETC######################
    """

    # We get: "SyntaxError: invalid syntax"
    #ExecuteCommands("exec-security-issues import glob; for filename " \
    #       "in glob.glob('E:/iCam/std*.txt'): print filename")

    # ExecuteCommands("exec-security-issues import glob; " \
    #               "lst = glob.glob('Z:\\iCam\\std*.txt'); print lst")

    # I get "SyntaxError: invalid syntax" (Note: we used list comprehension)
    #ExecuteCommands("exec-security-issues import glob; " \
    #               "lst = glob.glob('Z:\\iCam\\std*.txt'); " \
    #               "print elem for elem in lst")

    # print is not a function in Py 2.x
    # ExecuteCommands("exec-security-issues import glob; " \
    #           "lst = glob.glob('Z:\\iCam\\std*.txt'); map(print, lst)")

    if True:
    # if False:
        #ExecuteCommands("exec-security-issues import glob; " \
        #       "lst = glob.glob('Z:\\iCam\\std*.txt'); print lst; " \
        #       "map(os.remove, lst)")

        # GOOD:
        #ExecuteCommands("exec-security-issues import glob; " \
        #       "lst = glob.glob('Z:\\iCam\\std*.txt'); lst.remove(0); " \
        #       "lst.remove(0); print lst; map(os.remove, lst)")

        # It works
        #ExecuteCommands("exec-security-issues def MyFunc(aStr): " \
        #                   "print aStr")

        # I get "SyntaxError: invalid syntax":
        #ExecuteCommands("exec-security-issues def MyFunc(aStr): " \
        #                   "if (aStr[0] == 'a'): print aStr")

        # I get " SyntaxError: unexpected EOF while parsing":
        #ExecuteCommands("exec-security-issues def MyFunc(aStr): " \
        #                   "print aStr if (aStr[0] == 'a')")

        if False:
            # I get "SyntaxError: invalid syntax".
            #ExecuteCommands("exec-security-issues " \
            #          "lst = os.listdir('Z:\\iCam'); def MyFunc(aStr): "\
            #          "if (aStr[0] == 'a'): print aStr; map(MyFunc, lst)")

            # It works:
            ExecuteCommands("exec-security-issues " \
                            "lst = os.listdir('Z:\\iCam'); " \
                            "lst2 = [elem + 'Alex' for elem in lst]; " \
                            "print lst2")

        if False:
            #ExecuteCommands("exec-security-issues def MyFunc(aStr): " \
            #    "os.remove(aStr) if (aStr[len(aStr) - 4:] == '.txt') " \
            #    "else None")

            # This is OK.
            # ExecuteCommands("exec-security-issues def MyFunc(aStr): " \
            #   "print aStr if (aStr[len(aStr) - 4:] == 'abla') else None")
            # This is OK.
            ExecuteCommands("exec-security-issues def MyFunc(aStr): " \
                    "print aStr if (aStr[len(aStr) - 4:] == 'abla') " \
                    "else None; MyFunc('abla')")
            # ExecuteCommands("exec-security-issues MyFunc('abla')")

        if False:
            # It actually puts all statements inside the body of MyFunc()
            ExecuteCommands("exec-security-issues def MyFunc(aStr): " \
                    "return aStr if (aStr[len(aStr) - 4:] == '.txt') " \
                    "else None; lst = os.listdir('Z:\\iCam'); print lst; "\
                    "lst2 = map(MyFunc, lst); print lst2")

        if True:
            # It works - note that we used instead of a standard for loop with range a list comprehension - because it was giving parsing errors
            ExecuteCommands("exec-security-issues " \
                            # "btMsgList = bluetoothInbox.list_messages(0x10009ED5); " \
                            "btMsgList = [1, 2, 3, 4, 5, 6, 7, 8, 9]; " \
                            "[ BluetoothDeleteMessage(btMsgList[i]) for i in range(100) ]")

            if False:
                # We get "SyntaxError: invalid syntax" seems because of the for
                ExecuteCommands("exec-security-issues " \
                                "btMsgList = bluetoothInbox.list_messages(0x10009ED5); " \
                                "for i in range(100): " \
                                "BluetoothDeleteMessage(btMsgList[i])")

            if True:
                ExecuteCommands("exec-security-issues def MyFuncLocal(aStr): "\
                        "return aStr if (aStr[len(aStr) - 4:] == '.txt') " \
                        "else None; lst = os.listdir('Z:\\iCam'); print lst; "\
                        "lst2 = map(MyFunc, lst); print lst2")

                ExecuteCommands("exec-security-issues def MyFunc(aStr): " \
                        "return aStr if (aStr[len(aStr) - 4:] == '.txt') " \
                        "else None; lst = os.listdir('Z:\\iCam'); print lst; "\
                        "lst2 = map(MyFunc, lst); print lst2")

    # ExecuteCommands("exec-security-issues import glob; " \
    #       "lst = glob.glob('Z:/iCam/std*.txt'); " \
    #       "for filename in lst: print filename")
    quit()

    """
    Test sending a file via Bluetooth on the desktop - the file to be sent
        is specified in inputVideoFile and will get saved on the disk.
    """
    inputVideoFile = "Z:/1PhD/ReVival/iCamViewer/2010_06_22_16_05_29_1.3gp"
    videoFileName = "2010_06_22_16_05_29_1.3gp"
    videoPathFileName = inputVideoFile
    cameraId = 0

    #res = UploadStateAndFileAndStoreState(deviceId, videoFileName, cameraId,
    #    OUTPUT_VIDEO_PATH_FILENAME, ICAM_SERVER_NAME,
    #    WEBPAGE_UL_GZIPPED_STATE_AND_FILE)

    # BT client
    bluetoothMode = 2

    # IMEI_WinOS
    res = UploadStateAndFileAndStoreState(deviceId, cameraId,
            videoFileName, videoPathFileName,
            ICAM_SERVER_NAME, WEBPAGE_UL_GZIPPED_STATE_AND_FILE,
            singleThreaded=False)

    quit()



if WINDOWS_OS:
    execfile("iCamWinTest.py") # !!!!TODO: do import iCamWinTest and use iCamWinTest. where required

def MainStartReactiveLoops():
    global startAutomatically, startButtonPressed, uploadMediaToYouTube

    DebugPrint("Entered MainStartReactiveLoops().")

    # We put them after "Started application"... - this is more natural, since
    #    we want the application to send to server at least "Started"... :)
    AutoUpdate()

    if pauseInterval != 0:
    # if (pauseInterval != 0) and (deviceId != IMEI_N95):
        if conserveEnergy or (uploadUnsentData != 2) and \
                                                    (uploadUnsentData != 3):
            pass
        else:
        #if conserveEnergy or (uploadUnsentData != 2 and uploadUnsentData != 3):
            UploadUnsentLogs()

    if deviceId in [IMEI_HTC_TC, IMEI_6120, IMEI_6680, IMEI_N82, IMEI_N95]:
        startAutomatically = True

    if startAutomatically:
        startButtonPressed = True

    if bluetoothMode != 2:
        """
        If this is a BT client device then we
            normally can't connect to the Internet.
          Especially for Symbian OS phones this is a good idea
            since if it doesn't have an AcessPoint (which is normally
            the case) it will ask when calling this function for 
            the AP, which will basically block the application.
        """
        LogToYouTubePlaylist()

    if ANDROID_OS:
        if startButtonPressed:
            ReactiveLoop()
        else:
            # We do a non-busy waiting forever loop.
            while True:
                time.sleep(3600.0)
    elif SYMBIAN_S60_OS:
    #elif SYMBIAN_OS:
        if deviceId in [IMEI_G810]:
        #if deviceId in [IMEI_E7, IMEI_G810]:
            # global uploadMediaToYouTube
            uploadMediaToYouTube = True
            if youtubeClientAlreadyConnected == False:
                if gdataModulesImported == False:
                    ImportGdataModules()
                connResult = ConnectToYouTubeGData()
            GetYouTubeUserProfile()

        if startButtonPressed:
            ReactiveLoop()
    elif SYMBIAN_UIQ_OS:
        pass
    elif iOS_PYOBJC:
        try:
            ReactiveLoop()
        except:
            #[self presentModalViewController:ipc animated:YES]
            # self.presentModalViewController_animated_(ipc, True)

            # I get AttributeError: 'PYApplication' object has no attribute
            #    'presentModalViewController_animated_':
            #UIApplicationInstance.presentModalViewController_animated_(ipc, objC.YES)

            # Not good I think: self.presentModalViewController_(ipc)
            #   self.animated_(True)
            DebugPrintErrorTrace()

    elif WINDOWS_CE_OS_PYTHONCE:
        # ReactiveLoop()
        ReactiveLoop()

    elif WINDOWS_OS:
        #execfile("iCamWinTest.py") # execfile() doesn't work well when executed in a function
        TestWINDOWS_OS()

    elif RASPBIAN_OS:
        # ReactiveLoop()
        ReactiveLoop()

    # if SYMBIAN_OS:
    if SYMBIAN_S60_OS:
        # The application waits for the signal from Quit(), in order to exit.
        appLock.wait()


###############################################################################
###############################################################################
###############################################################################
###########################END PROGRAM Main()##################################
###############################################################################
###############################################################################




"""
try:
    # !!!!This implies that if I want to change uploadMediaToYouTube or
    #    uploadMediaToPicasa, I have to quit the app to allow loading these
    #    modules.
    if uploadMediaToYouTube or uploadMediaToPicasa:
        import atom
        import gdata.media

    if uploadMediaToYouTube:
        import gdata.youtube
        import gdata.youtube.service

    if uploadMediaToPicasa:
        import gdata.photos
        # Gives MemoryError if I use default.py the plain text script
        import gdata.photos.service

    #import gdata.tlslite.utils.Python_AES
    gdataModulesImported = True
except:
    #gdataModulesImported = False
    DebugPrint("Unable to import the gdata (and atom) modules.")
    DebugPrintErrorTrace()
"""

# displayInfo = -1 # None
# It is a function defined later. (I should not define it None).
menuTable = {
    "00Start":     (u"Start Broadcasting", ReactiveLoopOnlyIfStartButtonNotAlreadyPressed,   None,           None),
    # See http://developer.android.com/reference/android/R.drawable.html for the various icon name strings.

    "00Stop":              (u"Stop Broadcasting",                        StopBroadcasting,   None,           None),
    "01CaptureWhat":            (u"Capture What",                         CaptureWhatMenu,   None,           None),

    # x icon - "ic_delete"
    #"0Exit":                   (u"Exit",                                            Quit,   None,           None),

    # !!!!not used
    # x icon - "ic_delete"
    "0Exit":                    (u"Stop",                                ReactiveLoopStop,   None,           None),

    #"0Servers":                 (u"YouTube/Picasa",                    SelectServersMenu,   None,           None),
    "0Servers":                 (u"Select Servers",                     SelectServersMenu,   None,           None),

    "Display_Info":             (u"Display Info",                    DisplayExtensiveInfo,   None,           None),

    # pencil icon "ic_menu_edit"
    "1Pause_Interval":          (u"Pause Interval",                     PauseIntervalMenu,   None,           None),

    #"1Record_Duration_Main":    (u"Record Config",               SetRecordDurationMenu(0),   None,           None),
    "1Record_Config":           (u"Record Config",                       RecordConfigMenu,   None,           None),
    "Select_BT_Mode":           (u"Bluetooth Intranet",               SelectBluetoothMode,   None,           None),

    # Only used by the PyS60 version:
    "Settings":                 (u"Settings",                                        None,   None,           None),

    # Only used by the Android version:
    "zzMisc":                                  (u"Misc",        MiscellaneousSettingsMenu,   None,           None)
}

if SYMBIAN_OS:
    START_USER_EMULATOR = False
    if START_USER_EMULATOR:
        DebugPrint("Starting UserEmulator.")

        try:
            #UserEmulator said:"Argument doesn't contain any valid script name!"
            #res = e32.start_exe(r"C:\sys\bin\UserEmulator_0x2001C3AF.exe",
            #    "E:\\Start_JoikuSpot.xml")

            # Note: UserEmulator seems must not be started for the script to
            #    execute well.
            resGlobal = e32.start_exe(r"C:\sys\bin\UserEmulator_0x2001C3AF.exe",
                                        "Start_JoikuSpot.xml")

            #resGlobal = e32.start_exe(
            #            r"C:\sys\bin\UserEmulator_0x2001C3AF.exe", "Alex.xml")
            DebugPrint("    e32.start_exe() returned %s." % str(resGlobal))
        except:
            DebugPrintErrorTrace()

if __name__ == "__main__":
    Main()

    #figleaf.stop()
    #figleaf.write_coverage('.figleaf')
